showdown-rails 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2de1e46c86ff19fd34dc916bbfac4c00286fb2f
4
- data.tar.gz: 27cad48a4b1cce8947ca5d3b9a74b2725b0b5053
3
+ metadata.gz: 8fc5dd483f54514c93813984bd441cf47623d695
4
+ data.tar.gz: b2e0c458ffe546334a6226852841ffcddd63aa45
5
5
  SHA512:
6
- metadata.gz: 60b10f7c0bd690aa5a50e2794bd5eaf2989fe823c40083ed5da6eb338f33dd3897ad7d5f5b5fc0308bdbaf93307a1340f61237424dadee5a6f49ea9a25d6c626
7
- data.tar.gz: 1a4826cc693988807fcf65c999635616c9675bc0427f033f0145739a1a18d8bfb9058d73302e0c695a5ddb9f25178b6e6f54d7294ccd8670db3bdbb98582e21e
6
+ metadata.gz: 060fdb0ff0d9f1f402438f2c0019deafc1ffc228accf6c14fe099b02eba7f382457d41937cdfb7b2e4503cd56d6db4b951d64c007255a2bc097ed259964e2ce7
7
+ data.tar.gz: 4d1af9acf38a80c08a167633eb0edd67c948bfc5c1685c6522fad5b4632da1bd62869ddc503d8089a4ddc72b0a61b039b8ae2bd536a91a44e937c3a751c78918
data/README.md CHANGED
@@ -34,7 +34,7 @@ Or install it yourself as:
34
34
  2. Turn Markdown into HTML!
35
35
 
36
36
  ```
37
- var converter = new Showdown.converter()
37
+ var converter = new showdown.Converter()
38
38
  converter.makeHtml("** Markdown! **")
39
39
  ```
40
40
 
@@ -1,5 +1,5 @@
1
1
  module Showdown
2
2
  module Rails
3
- VERSION = "0.0.4"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -51,7 +51,7 @@
51
51
  //
52
52
  // var text = "Markdown *rocks*.";
53
53
  //
54
- // var converter = new Showdown.converter();
54
+ // var converter = new showdown.Converter();
55
55
  // var html = converter.makeHtml(text);
56
56
  //
57
57
  // alert(html);
@@ -60,1395 +60,2454 @@
60
60
  // file before uncommenting it.
61
61
  //
62
62
 
63
+ ;/*! showdown 25-11-2016 */
64
+ (function(){
65
+ /**
66
+ * Created by Tivie on 13-07-2015.
67
+ */
68
+
69
+ function getDefaultOpts(simple) {
70
+ 'use strict';
71
+
72
+ var defaultOptions = {
73
+ omitExtraWLInCodeBlocks: {
74
+ defaultValue: false,
75
+ describe: 'Omit the default extra whiteline added to code blocks',
76
+ type: 'boolean'
77
+ },
78
+ noHeaderId: {
79
+ defaultValue: false,
80
+ describe: 'Turn on/off generated header id',
81
+ type: 'boolean'
82
+ },
83
+ prefixHeaderId: {
84
+ defaultValue: false,
85
+ describe: 'Specify a prefix to generated header ids',
86
+ type: 'string'
87
+ },
88
+ headerLevelStart: {
89
+ defaultValue: false,
90
+ describe: 'The header blocks level start',
91
+ type: 'integer'
92
+ },
93
+ parseImgDimensions: {
94
+ defaultValue: false,
95
+ describe: 'Turn on/off image dimension parsing',
96
+ type: 'boolean'
97
+ },
98
+ simplifiedAutoLink: {
99
+ defaultValue: false,
100
+ describe: 'Turn on/off GFM autolink style',
101
+ type: 'boolean'
102
+ },
103
+ literalMidWordUnderscores: {
104
+ defaultValue: false,
105
+ describe: 'Parse midword underscores as literal underscores',
106
+ type: 'boolean'
107
+ },
108
+ strikethrough: {
109
+ defaultValue: false,
110
+ describe: 'Turn on/off strikethrough support',
111
+ type: 'boolean'
112
+ },
113
+ tables: {
114
+ defaultValue: false,
115
+ describe: 'Turn on/off tables support',
116
+ type: 'boolean'
117
+ },
118
+ tablesHeaderId: {
119
+ defaultValue: false,
120
+ describe: 'Add an id to table headers',
121
+ type: 'boolean'
122
+ },
123
+ ghCodeBlocks: {
124
+ defaultValue: true,
125
+ describe: 'Turn on/off GFM fenced code blocks support',
126
+ type: 'boolean'
127
+ },
128
+ tasklists: {
129
+ defaultValue: false,
130
+ describe: 'Turn on/off GFM tasklist support',
131
+ type: 'boolean'
132
+ },
133
+ smoothLivePreview: {
134
+ defaultValue: false,
135
+ describe: 'Prevents weird effects in live previews due to incomplete input',
136
+ type: 'boolean'
137
+ },
138
+ smartIndentationFix: {
139
+ defaultValue: false,
140
+ description: 'Tries to smartly fix indentation in es6 strings',
141
+ type: 'boolean'
142
+ },
143
+ disableForced4SpacesIndentedSublists: {
144
+ defaultValue: false,
145
+ description: 'Disables the requirement of indenting nested sublists by 4 spaces',
146
+ type: 'boolean'
147
+ }
148
+ };
149
+ if (simple === false) {
150
+ return JSON.parse(JSON.stringify(defaultOptions));
151
+ }
152
+ var ret = {};
153
+ for (var opt in defaultOptions) {
154
+ if (defaultOptions.hasOwnProperty(opt)) {
155
+ ret[opt] = defaultOptions[opt].defaultValue;
156
+ }
157
+ }
158
+ return ret;
159
+ }
63
160
 
64
- //
65
- // Showdown namespace
66
- //
67
- var Showdown = { extensions: {} };
68
-
69
- //
70
- // forEach
71
- //
72
- var forEach = Showdown.forEach = function(obj, callback) {
73
- if (typeof obj.forEach === 'function') {
74
- obj.forEach(callback);
75
- } else {
76
- var i, len = obj.length;
77
- for (i = 0; i < len; i++) {
78
- callback(obj[i], i, obj);
79
- }
80
- }
161
+ /**
162
+ * Created by Tivie on 06-01-2015.
163
+ */
164
+
165
+ // Private properties
166
+ var showdown = {},
167
+ parsers = {},
168
+ extensions = {},
169
+ globalOptions = getDefaultOpts(true),
170
+ flavor = {
171
+ github: {
172
+ omitExtraWLInCodeBlocks: true,
173
+ prefixHeaderId: 'user-content-',
174
+ simplifiedAutoLink: true,
175
+ literalMidWordUnderscores: true,
176
+ strikethrough: true,
177
+ tables: true,
178
+ tablesHeaderId: true,
179
+ ghCodeBlocks: true,
180
+ tasklists: true,
181
+ disableForced4SpacesIndentedSublists: true
182
+ },
183
+ vanilla: getDefaultOpts(true)
184
+ };
185
+
186
+ /**
187
+ * helper namespace
188
+ * @type {{}}
189
+ */
190
+ showdown.helper = {};
191
+
192
+ /**
193
+ * TODO LEGACY SUPPORT CODE
194
+ * @type {{}}
195
+ */
196
+ showdown.extensions = {};
197
+
198
+ /**
199
+ * Set a global option
200
+ * @static
201
+ * @param {string} key
202
+ * @param {*} value
203
+ * @returns {showdown}
204
+ */
205
+ showdown.setOption = function (key, value) {
206
+ 'use strict';
207
+ globalOptions[key] = value;
208
+ return this;
81
209
  };
82
210
 
83
- //
84
- // Standard extension naming
85
- //
86
- var stdExtName = function(s) {
87
- return s.replace(/[_-]||\s/g, '').toLowerCase();
211
+ /**
212
+ * Get a global option
213
+ * @static
214
+ * @param {string} key
215
+ * @returns {*}
216
+ */
217
+ showdown.getOption = function (key) {
218
+ 'use strict';
219
+ return globalOptions[key];
88
220
  };
89
221
 
90
- //
91
- // converter
92
- //
93
- // Wraps all "globals" so that the only thing
94
- // exposed is makeHtml().
95
- //
96
- Showdown.converter = function(converter_options) {
97
-
98
- //
99
- // Globals:
100
- //
101
-
102
- // Global hashes, used by various utility routines
103
- var g_urls;
104
- var g_titles;
105
- var g_html_blocks;
106
-
107
- // Used to track when we're inside an ordered or unordered list
108
- // (see _ProcessListItems() for details):
109
- var g_list_level = 0;
110
-
111
- // Global extensions
112
- var g_lang_extensions = [];
113
- var g_output_modifiers = [];
114
-
115
-
116
- //
117
- // Automatic Extension Loading (node only):
118
- //
119
-
120
- if (typeof module !== 'undefind' && typeof exports !== 'undefined' && typeof require !== 'undefind') {
121
- var fs = require('fs');
122
-
123
- if (fs) {
124
- // Search extensions folder
125
- var extensions = fs.readdirSync((__dirname || '.')+'/extensions').filter(function(file){
126
- return ~file.indexOf('.js');
127
- }).map(function(file){
128
- return file.replace(/\.js$/, '');
129
- });
130
- // Load extensions into Showdown namespace
131
- Showdown.forEach(extensions, function(ext){
132
- var name = stdExtName(ext);
133
- Showdown.extensions[name] = require('./extensions/' + ext);
134
- });
135
- }
136
- }
137
-
138
- this.makeHtml = function(text) {
139
- //
140
- // Main function. The order in which other subs are called here is
141
- // essential. Link and image substitutions need to happen before
142
- // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
143
- // and <img> tags get encoded.
144
- //
145
-
146
- // Clear the global hashes. If we don't clear these, you get conflicts
147
- // from other articles when generating a page which contains more than
148
- // one article (e.g. an index page that shows the N most recent
149
- // articles):
150
- g_urls = {};
151
- g_titles = {};
152
- g_html_blocks = [];
153
-
154
- // attacklab: Replace ~ with ~T
155
- // This lets us use tilde as an escape char to avoid md5 hashes
156
- // The choice of character is arbitray; anything that isn't
157
- // magic in Markdown will work.
158
- text = text.replace(/~/g,"~T");
159
-
160
- // attacklab: Replace $ with ~D
161
- // RegExp interprets $ as a special character
162
- // when it's in a replacement string
163
- text = text.replace(/\$/g,"~D");
164
-
165
- // Standardize line endings
166
- text = text.replace(/\r\n/g,"\n"); // DOS to Unix
167
- text = text.replace(/\r/g,"\n"); // Mac to Unix
168
-
169
- // Make sure text begins and ends with a couple of newlines:
170
- text = "\n\n" + text + "\n\n";
171
-
172
- // Convert all tabs to spaces.
173
- text = _Detab(text);
174
-
175
- // Strip any lines consisting only of spaces and tabs.
176
- // This makes subsequent regexen easier to write, because we can
177
- // match consecutive blank lines with /\n+/ instead of something
178
- // contorted like /[ \t]*\n+/ .
179
- text = text.replace(/^[ \t]+$/mg,"");
180
-
181
- // Run language extensions
182
- Showdown.forEach(g_lang_extensions, function(x){
183
- text = _ExecuteExtension(x, text);
184
- });
185
-
186
- // Handle github codeblocks prior to running HashHTML so that
187
- // HTML contained within the codeblock gets escaped propertly
188
- text = _DoGithubCodeBlocks(text);
189
-
190
- // Turn block-level HTML blocks into hash entries
191
- text = _HashHTMLBlocks(text);
192
-
193
- // Strip link definitions, store in hashes.
194
- text = _StripLinkDefinitions(text);
195
-
196
- text = _RunBlockGamut(text);
197
-
198
- text = _UnescapeSpecialChars(text);
199
-
200
- // attacklab: Restore dollar signs
201
- text = text.replace(/~D/g,"$$");
202
-
203
- // attacklab: Restore tildes
204
- text = text.replace(/~T/g,"~");
205
-
206
- // Run output modifiers
207
- Showdown.forEach(g_output_modifiers, function(x){
208
- text = _ExecuteExtension(x, text);
209
- });
210
-
211
- return text;
222
+ /**
223
+ * Get the global options
224
+ * @static
225
+ * @returns {{}}
226
+ */
227
+ showdown.getOptions = function () {
228
+ 'use strict';
229
+ return globalOptions;
212
230
  };
213
- //
214
- // Options:
215
- //
216
231
 
217
- // Parse extensions options into separate arrays
218
- if (converter_options && converter_options.extensions) {
219
-
220
- var self = this;
221
-
222
- // Iterate over each plugin
223
- Showdown.forEach(converter_options.extensions, function(plugin){
224
-
225
- // Assume it's a bundled plugin if a string is given
226
- if (typeof plugin === 'string') {
227
- plugin = Showdown.extensions[stdExtName(plugin)];
228
- }
229
-
230
- if (typeof plugin === 'function') {
231
- // Iterate over each extension within that plugin
232
- Showdown.forEach(plugin(self), function(ext){
233
- // Sort extensions by type
234
- if (ext.type) {
235
- if (ext.type === 'language' || ext.type === 'lang') {
236
- g_lang_extensions.push(ext);
237
- } else if (ext.type === 'output' || ext.type === 'html') {
238
- g_output_modifiers.push(ext);
239
- }
240
- } else {
241
- // Assume language extension
242
- g_output_modifiers.push(ext);
243
- }
244
- });
245
- } else {
246
- throw "Extension '" + plugin + "' could not be loaded. It was either not found or is not a valid extension.";
247
- }
248
- });
249
- }
250
-
251
-
252
- var _ExecuteExtension = function(ext, text) {
253
- if (ext.regex) {
254
- var re = new RegExp(ext.regex, 'g');
255
- return text.replace(re, ext.replace);
256
- } else if (ext.filter) {
257
- return ext.filter(text);
258
- }
232
+ /**
233
+ * Reset global options to the default values
234
+ * @static
235
+ */
236
+ showdown.resetOptions = function () {
237
+ 'use strict';
238
+ globalOptions = getDefaultOpts(true);
259
239
  };
260
240
 
261
- var _StripLinkDefinitions = function(text) {
262
- //
263
- // Strips link definitions from text, stores the URLs and titles in
264
- // hash references.
265
- //
266
-
267
- // Link defs are in the form: ^[id]: url "optional title"
268
-
269
- /*
270
- var text = text.replace(/
271
- ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
272
- [ \t]*
273
- \n? // maybe *one* newline
274
- [ \t]*
275
- <?(\S+?)>? // url = $2
276
- [ \t]*
277
- \n? // maybe one newline
278
- [ \t]*
279
- (?:
280
- (\n*) // any lines skipped = $3 attacklab: lookbehind removed
281
- ["(]
282
- (.+?) // title = $4
283
- [")]
284
- [ \t]*
285
- )? // title is optional
286
- (?:\n+|$)
287
- /gm,
288
- function(){...});
289
- */
290
-
291
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
292
- text += "~0";
293
-
294
- text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm,
295
- function (wholeMatch,m1,m2,m3,m4) {
296
- m1 = m1.toLowerCase();
297
- g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive
298
- if (m3) {
299
- // Oops, found blank lines, so it's not a title.
300
- // Put back the parenthetical statement we stole.
301
- return m3+m4;
302
- } else if (m4) {
303
- g_titles[m1] = m4.replace(/"/g,"&quot;");
304
- }
305
-
306
- // Completely remove the definition from the text
307
- return "";
308
- }
309
- );
310
-
311
- // attacklab: strip sentinel
312
- text = text.replace(/~0/,"");
313
-
314
- return text;
315
- }
316
-
317
-
318
- var _HashHTMLBlocks = function(text) {
319
- // attacklab: Double up blank lines to reduce lookaround
320
- text = text.replace(/\n/g,"\n\n");
321
-
322
- // Hashify HTML blocks:
323
- // We only want to do this for block-level HTML tags, such as headers,
324
- // lists, and tables. That's because we still want to wrap <p>s around
325
- // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
326
- // phrase emphasis, and spans. The list of tags we're looking for is
327
- // hard-coded:
328
- var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside";
329
- var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside";
330
-
331
- // First, look for nested blocks, e.g.:
332
- // <div>
333
- // <div>
334
- // tags for inner block must be indented.
335
- // </div>
336
- // </div>
337
- //
338
- // The outermost tags must start at the left margin for this to match, and
339
- // the inner nested divs must be indented.
340
- // We need to do this before the next, more liberal match, because the next
341
- // match will start at the first `<div>` and stop at the first `</div>`.
342
-
343
- // attacklab: This regex can be expensive when it fails.
344
- /*
345
- var text = text.replace(/
346
- ( // save in $1
347
- ^ // start of line (with /m)
348
- <($block_tags_a) // start tag = $2
349
- \b // word break
350
- // attacklab: hack around khtml/pcre bug...
351
- [^\r]*?\n // any number of lines, minimally matching
352
- </\2> // the matching end tag
353
- [ \t]* // trailing spaces/tabs
354
- (?=\n+) // followed by a newline
355
- ) // attacklab: there are sentinel newlines at end of document
356
- /gm,function(){...}};
357
- */
358
- text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement);
359
-
360
- //
361
- // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
362
- //
363
-
364
- /*
365
- var text = text.replace(/
366
- ( // save in $1
367
- ^ // start of line (with /m)
368
- <($block_tags_b) // start tag = $2
369
- \b // word break
370
- // attacklab: hack around khtml/pcre bug...
371
- [^\r]*? // any number of lines, minimally matching
372
- </\2> // the matching end tag
373
- [ \t]* // trailing spaces/tabs
374
- (?=\n+) // followed by a newline
375
- ) // attacklab: there are sentinel newlines at end of document
376
- /gm,function(){...}};
377
- */
378
- text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);
379
-
380
- // Special case just for <hr />. It was easier to make a special case than
381
- // to make the other regex more complicated.
382
-
383
- /*
384
- text = text.replace(/
385
- ( // save in $1
386
- \n\n // Starting after a blank line
387
- [ ]{0,3}
388
- (<(hr) // start tag = $2
389
- \b // word break
390
- ([^<>])*? //
391
- \/?>) // the matching end tag
392
- [ \t]*
393
- (?=\n{2,}) // followed by a blank line
394
- )
395
- /g,hashElement);
396
- */
397
- text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
398
-
399
- // Special case for standalone HTML comments:
400
-
401
- /*
402
- text = text.replace(/
403
- ( // save in $1
404
- \n\n // Starting after a blank line
405
- [ ]{0,3} // attacklab: g_tab_width - 1
406
- <!
407
- (--[^\r]*?--\s*)+
408
- >
409
- [ \t]*
410
- (?=\n{2,}) // followed by a blank line
411
- )
412
- /g,hashElement);
413
- */
414
- text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
415
-
416
- // PHP and ASP-style processor instructions (<?...?> and <%...%>)
417
-
418
- /*
419
- text = text.replace(/
420
- (?:
421
- \n\n // Starting after a blank line
422
- )
423
- ( // save in $1
424
- [ ]{0,3} // attacklab: g_tab_width - 1
425
- (?:
426
- <([?%]) // $2
427
- [^\r]*?
428
- \2>
429
- )
430
- [ \t]*
431
- (?=\n{2,}) // followed by a blank line
432
- )
433
- /g,hashElement);
434
- */
435
- text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
436
-
437
- // attacklab: Undo double lines (see comment at top of this function)
438
- text = text.replace(/\n\n/g,"\n");
439
- return text;
440
- }
441
-
442
- var hashElement = function(wholeMatch,m1) {
443
- var blockText = m1;
444
-
445
- // Undo double lines
446
- blockText = blockText.replace(/\n\n/g,"\n");
447
- blockText = blockText.replace(/^\n/,"");
448
-
449
- // strip trailing blank lines
450
- blockText = blockText.replace(/\n+$/g,"");
451
-
452
- // Replace the element text with a marker ("~KxK" where x is its key)
453
- blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
454
-
455
- return blockText;
241
+ /**
242
+ * Set the flavor showdown should use as default
243
+ * @param {string} name
244
+ */
245
+ showdown.setFlavor = function (name) {
246
+ 'use strict';
247
+ if (flavor.hasOwnProperty(name)) {
248
+ var preset = flavor[name];
249
+ for (var option in preset) {
250
+ if (preset.hasOwnProperty(option)) {
251
+ globalOptions[option] = preset[option];
252
+ }
253
+ }
254
+ }
456
255
  };
457
256
 
458
- var _RunBlockGamut = function(text) {
459
- //
460
- // These are all the transformations that form block-level
461
- // tags like paragraphs, headers, and list items.
462
- //
463
- text = _DoHeaders(text);
464
-
465
- // Do Horizontal Rules:
466
- var key = hashBlock("<hr />");
467
- text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
468
- text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
469
- text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
470
-
471
- text = _DoLists(text);
472
- text = _DoCodeBlocks(text);
473
- text = _DoBlockQuotes(text);
474
-
475
- // We already ran _HashHTMLBlocks() before, in Markdown(), but that
476
- // was to escape raw HTML in the original Markdown source. This time,
477
- // we're escaping the markup we've just created, so that we don't wrap
478
- // <p> tags around block-level tags.
479
- text = _HashHTMLBlocks(text);
480
- text = _FormParagraphs(text);
481
-
482
- return text;
257
+ /**
258
+ * Get the default options
259
+ * @static
260
+ * @param {boolean} [simple=true]
261
+ * @returns {{}}
262
+ */
263
+ showdown.getDefaultOptions = function (simple) {
264
+ 'use strict';
265
+ return getDefaultOpts(simple);
483
266
  };
484
267
 
485
-
486
- var _RunSpanGamut = function(text) {
487
- //
488
- // These are all the transformations that occur *within* block-level
489
- // tags like paragraphs, headers, and list items.
490
- //
491
-
492
- text = _DoCodeSpans(text);
493
- text = _EscapeSpecialCharsWithinTagAttributes(text);
494
- text = _EncodeBackslashEscapes(text);
495
-
496
- // Process anchor and image tags. Images must come first,
497
- // because ![foo][f] looks like an anchor.
498
- text = _DoImages(text);
499
- text = _DoAnchors(text);
500
-
501
- // Make links out of things like `<http://example.com/>`
502
- // Must come after _DoAnchors(), because you can use < and >
503
- // delimiters in inline links like [this](<url>).
504
- text = _DoAutoLinks(text);
505
- text = _EncodeAmpsAndAngles(text);
506
- text = _DoItalicsAndBold(text);
507
-
508
- // Do hard breaks:
509
- text = text.replace(/ +\n/g," <br />\n");
510
-
511
- return text;
512
- }
513
-
514
- var _EscapeSpecialCharsWithinTagAttributes = function(text) {
515
- //
516
- // Within tags -- meaning between < and > -- encode [\ ` * _] so they
517
- // don't conflict with their use in Markdown for code, italics and strong.
518
- //
519
-
520
- // Build a regex to find HTML tags and comments. See Friedl's
521
- // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
522
- var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
523
-
524
- text = text.replace(regex, function(wholeMatch) {
525
- var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
526
- tag = escapeCharacters(tag,"\\`*_");
527
- return tag;
528
- });
529
-
530
- return text;
531
- }
532
-
533
- var _DoAnchors = function(text) {
534
- //
535
- // Turn Markdown link shortcuts into XHTML <a> tags.
536
- //
537
- //
538
- // First, handle reference-style links: [link text] [id]
539
- //
540
-
541
- /*
542
- text = text.replace(/
543
- ( // wrap whole match in $1
544
- \[
545
- (
546
- (?:
547
- \[[^\]]*\] // allow brackets nested one level
548
- |
549
- [^\[] // or anything else
550
- )*
551
- )
552
- \]
553
-
554
- [ ]? // one optional space
555
- (?:\n[ ]*)? // one optional newline followed by spaces
556
-
557
- \[
558
- (.*?) // id = $3
559
- \]
560
- )()()()() // pad remaining backreferences
561
- /g,_DoAnchors_callback);
562
- */
563
- text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
564
-
565
- //
566
- // Next, inline-style links: [link text](url "optional title")
567
- //
568
-
569
- /*
570
- text = text.replace(/
571
- ( // wrap whole match in $1
572
- \[
573
- (
574
- (?:
575
- \[[^\]]*\] // allow brackets nested one level
576
- |
577
- [^\[\]] // or anything else
578
- )
579
- )
580
- \]
581
- \( // literal paren
582
- [ \t]*
583
- () // no id, so leave $3 empty
584
- <?(.*?)>? // href = $4
585
- [ \t]*
586
- ( // $5
587
- (['"]) // quote char = $6
588
- (.*?) // Title = $7
589
- \6 // matching quote
590
- [ \t]* // ignore any spaces/tabs between closing quote and )
591
- )? // title is optional
592
- \)
593
- )
594
- /g,writeAnchorTag);
595
- */
596
- text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
597
-
598
- //
599
- // Last, handle reference-style shortcuts: [link text]
600
- // These must come last in case you've also got [link test][1]
601
- // or [link test](/foo)
602
- //
603
-
604
- /*
605
- text = text.replace(/
606
- ( // wrap whole match in $1
607
- \[
608
- ([^\[\]]+) // link text = $2; can't contain '[' or ']'
609
- \]
610
- )()()()()() // pad rest of backreferences
611
- /g, writeAnchorTag);
612
- */
613
- text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
614
-
615
- return text;
616
- }
617
-
618
- var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
619
- if (m7 == undefined) m7 = "";
620
- var whole_match = m1;
621
- var link_text = m2;
622
- var link_id = m3.toLowerCase();
623
- var url = m4;
624
- var title = m7;
625
-
626
- if (url == "") {
627
- if (link_id == "") {
628
- // lower-case and turn embedded newlines into spaces
629
- link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
630
- }
631
- url = "#"+link_id;
632
-
633
- if (g_urls[link_id] != undefined) {
634
- url = g_urls[link_id];
635
- if (g_titles[link_id] != undefined) {
636
- title = g_titles[link_id];
637
- }
638
- }
639
- else {
640
- if (whole_match.search(/\(\s*\)$/m)>-1) {
641
- // Special case for explicit empty url
642
- url = "";
643
- } else {
644
- return whole_match;
645
- }
646
- }
647
- }
648
-
649
- url = escapeCharacters(url,"*_");
650
- var result = "<a href=\"" + url + "\"";
651
-
652
- if (title != "") {
653
- title = title.replace(/"/g,"&quot;");
654
- title = escapeCharacters(title,"*_");
655
- result += " title=\"" + title + "\"";
656
- }
657
-
658
- result += ">" + link_text + "</a>";
659
-
660
- return result;
661
- }
662
-
663
-
664
- var _DoImages = function(text) {
665
- //
666
- // Turn Markdown image shortcuts into <img> tags.
667
- //
668
-
669
- //
670
- // First, handle reference-style labeled images: ![alt text][id]
671
- //
672
-
673
- /*
674
- text = text.replace(/
675
- ( // wrap whole match in $1
676
- !\[
677
- (.*?) // alt text = $2
678
- \]
679
-
680
- [ ]? // one optional space
681
- (?:\n[ ]*)? // one optional newline followed by spaces
682
-
683
- \[
684
- (.*?) // id = $3
685
- \]
686
- )()()()() // pad rest of backreferences
687
- /g,writeImageTag);
688
- */
689
- text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
690
-
691
- //
692
- // Next, handle inline images: ![alt text](url "optional title")
693
- // Don't forget: encode * and _
694
-
695
- /*
696
- text = text.replace(/
697
- ( // wrap whole match in $1
698
- !\[
699
- (.*?) // alt text = $2
700
- \]
701
- \s? // One optional whitespace character
702
- \( // literal paren
703
- [ \t]*
704
- () // no id, so leave $3 empty
705
- <?(\S+?)>? // src url = $4
706
- [ \t]*
707
- ( // $5
708
- (['"]) // quote char = $6
709
- (.*?) // title = $7
710
- \6 // matching quote
711
- [ \t]*
712
- )? // title is optional
713
- \)
714
- )
715
- /g,writeImageTag);
716
- */
717
- text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
718
-
719
- return text;
720
- }
721
-
722
- var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
723
- var whole_match = m1;
724
- var alt_text = m2;
725
- var link_id = m3.toLowerCase();
726
- var url = m4;
727
- var title = m7;
728
-
729
- if (!title) title = "";
730
-
731
- if (url == "") {
732
- if (link_id == "") {
733
- // lower-case and turn embedded newlines into spaces
734
- link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
735
- }
736
- url = "#"+link_id;
737
-
738
- if (g_urls[link_id] != undefined) {
739
- url = g_urls[link_id];
740
- if (g_titles[link_id] != undefined) {
741
- title = g_titles[link_id];
742
- }
743
- }
744
- else {
745
- return whole_match;
746
- }
747
- }
748
-
749
- alt_text = alt_text.replace(/"/g,"&quot;");
750
- url = escapeCharacters(url,"*_");
751
- var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
752
-
753
- // attacklab: Markdown.pl adds empty title attributes to images.
754
- // Replicate this bug.
755
-
756
- //if (title != "") {
757
- title = title.replace(/"/g,"&quot;");
758
- title = escapeCharacters(title,"*_");
759
- result += " title=\"" + title + "\"";
760
- //}
761
-
762
- result += " />";
763
-
764
- return result;
765
- }
766
-
767
-
768
- var _DoHeaders = function(text) {
769
-
770
- // Setext-style headers:
771
- // Header 1
772
- // ========
773
- //
774
- // Header 2
775
- // --------
776
- //
777
- text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
778
- function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});
779
-
780
- text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
781
- function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});
782
-
783
- // atx-style headers:
784
- // # Header 1
785
- // ## Header 2
786
- // ## Header 2 with closing hashes ##
787
- // ...
788
- // ###### Header 6
789
- //
790
-
791
- /*
792
- text = text.replace(/
793
- ^(\#{1,6}) // $1 = string of #'s
794
- [ \t]*
795
- (.+?) // $2 = Header text
796
- [ \t]*
797
- \#* // optional closing #'s (not counted)
798
- \n+
799
- /gm, function() {...});
800
- */
801
-
802
- text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
803
- function(wholeMatch,m1,m2) {
804
- var h_level = m1.length;
805
- return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
806
- });
807
-
808
- function headerId(m) {
809
- return m.replace(/[^\w]/g, '').toLowerCase();
810
- }
811
- return text;
812
- }
813
-
814
- // This declaration keeps Dojo compressor from outputting garbage:
815
- var _ProcessListItems;
816
-
817
- var _DoLists = function(text) {
818
- //
819
- // Form HTML ordered (numbered) and unordered (bulleted) lists.
820
- //
821
-
822
- // attacklab: add sentinel to hack around khtml/safari bug:
823
- // http://bugs.webkit.org/show_bug.cgi?id=11231
824
- text += "~0";
825
-
826
- // Re-usable pattern to match any entirel ul or ol list:
827
-
828
- /*
829
- var whole_list = /
830
- ( // $1 = whole list
831
- ( // $2
832
- [ ]{0,3} // attacklab: g_tab_width - 1
833
- ([*+-]|\d+[.]) // $3 = first list item marker
834
- [ \t]+
835
- )
836
- [^\r]+?
837
- ( // $4
838
- ~0 // sentinel for workaround; should be $
839
- |
840
- \n{2,}
841
- (?=\S)
842
- (?! // Negative lookahead for another list item marker
843
- [ \t]*
844
- (?:[*+-]|\d+[.])[ \t]+
845
- )
846
- )
847
- )/g
848
- */
849
- var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
850
-
851
- if (g_list_level) {
852
- text = text.replace(whole_list,function(wholeMatch,m1,m2) {
853
- var list = m1;
854
- var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
855
-
856
- // Turn double returns into triple returns, so that we can make a
857
- // paragraph for the last item in a list, if necessary:
858
- list = list.replace(/\n{2,}/g,"\n\n\n");;
859
- var result = _ProcessListItems(list);
860
-
861
- // Trim any trailing whitespace, to put the closing `</$list_type>`
862
- // up on the preceding line, to get it past the current stupid
863
- // HTML block parser. This is a hack to work around the terrible
864
- // hack that is the HTML block parser.
865
- result = result.replace(/\s+$/,"");
866
- result = "<"+list_type+">" + result + "</"+list_type+">\n";
867
- return result;
868
- });
869
- } else {
870
- whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
871
- text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
872
- var runup = m1;
873
- var list = m2;
874
-
875
- var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
876
- // Turn double returns into triple returns, so that we can make a
877
- // paragraph for the last item in a list, if necessary:
878
- var list = list.replace(/\n{2,}/g,"\n\n\n");;
879
- var result = _ProcessListItems(list);
880
- result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
881
- return result;
882
- });
883
- }
884
-
885
- // attacklab: strip sentinel
886
- text = text.replace(/~0/,"");
887
-
888
- return text;
889
- }
890
-
891
- _ProcessListItems = function(list_str) {
892
- //
893
- // Process the contents of a single ordered or unordered list, splitting it
894
- // into individual list items.
895
- //
896
- // The $g_list_level global keeps track of when we're inside a list.
897
- // Each time we enter a list, we increment it; when we leave a list,
898
- // we decrement. If it's zero, we're not in a list anymore.
899
- //
900
- // We do this because when we're not inside a list, we want to treat
901
- // something like this:
902
- //
903
- // I recommend upgrading to version
904
- // 8. Oops, now this line is treated
905
- // as a sub-list.
906
- //
907
- // As a single paragraph, despite the fact that the second line starts
908
- // with a digit-period-space sequence.
909
- //
910
- // Whereas when we're inside a list (or sub-list), that line will be
911
- // treated as the start of a sub-list. What a kludge, huh? This is
912
- // an aspect of Markdown's syntax that's hard to parse perfectly
913
- // without resorting to mind-reading. Perhaps the solution is to
914
- // change the syntax rules such that sub-lists must start with a
915
- // starting cardinal number; e.g. "1." or "a.".
916
-
917
- g_list_level++;
918
-
919
- // trim trailing blank lines:
920
- list_str = list_str.replace(/\n{2,}$/,"\n");
921
-
922
- // attacklab: add sentinel to emulate \z
923
- list_str += "~0";
924
-
925
- /*
926
- list_str = list_str.replace(/
927
- (\n)? // leading line = $1
928
- (^[ \t]*) // leading whitespace = $2
929
- ([*+-]|\d+[.]) [ \t]+ // list marker = $3
930
- ([^\r]+? // list item text = $4
931
- (\n{1,2}))
932
- (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
933
- /gm, function(){...});
934
- */
935
- list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
936
- function(wholeMatch,m1,m2,m3,m4){
937
- var item = m4;
938
- var leading_line = m1;
939
- var leading_space = m2;
940
-
941
- if (leading_line || (item.search(/\n{2,}/)>-1)) {
942
- item = _RunBlockGamut(_Outdent(item));
943
- }
944
- else {
945
- // Recursion for sub-lists:
946
- item = _DoLists(_Outdent(item));
947
- item = item.replace(/\n$/,""); // chomp(item)
948
- item = _RunSpanGamut(item);
949
- }
950
-
951
- return "<li>" + item + "</li>\n";
952
- }
953
- );
954
-
955
- // attacklab: strip sentinel
956
- list_str = list_str.replace(/~0/g,"");
957
-
958
- g_list_level--;
959
- return list_str;
960
- }
961
-
962
-
963
- var _DoCodeBlocks = function(text) {
964
- //
965
- // Process Markdown `<pre><code>` blocks.
966
- //
967
-
968
- /*
969
- text = text.replace(text,
970
- /(?:\n\n|^)
971
- ( // $1 = the code block -- one or more lines, starting with a space/tab
972
- (?:
973
- (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
974
- .*\n+
975
- )+
976
- )
977
- (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
978
- /g,function(){...});
979
- */
980
-
981
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
982
- text += "~0";
983
-
984
- text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
985
- function(wholeMatch,m1,m2) {
986
- var codeblock = m1;
987
- var nextChar = m2;
988
-
989
- codeblock = _EncodeCode( _Outdent(codeblock));
990
- codeblock = _Detab(codeblock);
991
- codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
992
- codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
993
-
994
- codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
995
-
996
- return hashBlock(codeblock) + nextChar;
997
- }
998
- );
999
-
1000
- // attacklab: strip sentinel
1001
- text = text.replace(/~0/,"");
1002
-
1003
- return text;
268
+ /**
269
+ * Get or set a subParser
270
+ *
271
+ * subParser(name) - Get a registered subParser
272
+ * subParser(name, func) - Register a subParser
273
+ * @static
274
+ * @param {string} name
275
+ * @param {function} [func]
276
+ * @returns {*}
277
+ */
278
+ showdown.subParser = function (name, func) {
279
+ 'use strict';
280
+ if (showdown.helper.isString(name)) {
281
+ if (typeof func !== 'undefined') {
282
+ parsers[name] = func;
283
+ } else {
284
+ if (parsers.hasOwnProperty(name)) {
285
+ return parsers[name];
286
+ } else {
287
+ throw Error('SubParser named ' + name + ' not registered!');
288
+ }
289
+ }
290
+ }
1004
291
  };
1005
292
 
1006
- var _DoGithubCodeBlocks = function(text) {
1007
- //
1008
- // Process Github-style code blocks
1009
- // Example:
1010
- // ```ruby
1011
- // def hello_world(x)
1012
- // puts "Hello, #{x}"
1013
- // end
1014
- // ```
1015
- //
1016
-
1017
-
1018
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
1019
- text += "~0";
1020
-
1021
- text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,
1022
- function(wholeMatch,m1,m2) {
1023
- var language = m1;
1024
- var codeblock = m2;
1025
-
1026
- codeblock = _EncodeCode(codeblock);
1027
- codeblock = _Detab(codeblock);
1028
- codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
1029
- codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
1030
-
1031
- codeblock = "<pre><code" + (language ? " class=\"" + language + '"' : "") + ">" + codeblock + "\n</code></pre>";
1032
-
1033
- return hashBlock(codeblock);
1034
- }
1035
- );
1036
-
1037
- // attacklab: strip sentinel
1038
- text = text.replace(/~0/,"");
1039
-
1040
- return text;
1041
- }
1042
-
1043
- var hashBlock = function(text) {
1044
- text = text.replace(/(^\n+|\n+$)/g,"");
1045
- return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
1046
- }
1047
-
1048
- var _DoCodeSpans = function(text) {
1049
- //
1050
- // * Backtick quotes are used for <code></code> spans.
1051
- //
1052
- // * You can use multiple backticks as the delimiters if you want to
1053
- // include literal backticks in the code span. So, this input:
1054
- //
1055
- // Just type ``foo `bar` baz`` at the prompt.
1056
- //
1057
- // Will translate to:
1058
- //
1059
- // <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
1060
- //
1061
- // There's no arbitrary limit to the number of backticks you
1062
- // can use as delimters. If you need three consecutive backticks
1063
- // in your code, use four for delimiters, etc.
1064
- //
1065
- // * You can use spaces to get literal backticks at the edges:
1066
- //
1067
- // ... type `` `bar` `` ...
1068
- //
1069
- // Turns to:
1070
- //
1071
- // ... type <code>`bar`</code> ...
1072
- //
1073
-
1074
- /*
1075
- text = text.replace(/
1076
- (^|[^\\]) // Character before opening ` can't be a backslash
1077
- (`+) // $2 = Opening run of `
1078
- ( // $3 = The code block
1079
- [^\r]*?
1080
- [^`] // attacklab: work around lack of lookbehind
1081
- )
1082
- \2 // Matching closer
1083
- (?!`)
1084
- /gm, function(){...});
1085
- */
1086
-
1087
- text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
1088
- function(wholeMatch,m1,m2,m3,m4) {
1089
- var c = m3;
1090
- c = c.replace(/^([ \t]*)/g,""); // leading whitespace
1091
- c = c.replace(/[ \t]*$/g,""); // trailing whitespace
1092
- c = _EncodeCode(c);
1093
- return m1+"<code>"+c+"</code>";
1094
- });
1095
-
1096
- return text;
1097
- }
1098
-
1099
- var _EncodeCode = function(text) {
1100
- //
1101
- // Encode/escape certain characters inside Markdown code runs.
1102
- // The point is that in code, these characters are literals,
1103
- // and lose their special Markdown meanings.
1104
- //
1105
- // Encode all ampersands; HTML entities are not
1106
- // entities within a Markdown code span.
1107
- text = text.replace(/&/g,"&amp;");
1108
-
1109
- // Do the angle bracket song and dance:
1110
- text = text.replace(/</g,"&lt;");
1111
- text = text.replace(/>/g,"&gt;");
1112
-
1113
- // Now, escape characters that are magic in Markdown:
1114
- text = escapeCharacters(text,"\*_{}[]\\",false);
1115
-
1116
- // jj the line above breaks this:
1117
- //---
1118
-
1119
- //* Item
1120
-
1121
- // 1. Subitem
1122
-
1123
- // special char: *
1124
- //---
1125
-
1126
- return text;
1127
- }
1128
-
1129
-
1130
- var _DoItalicsAndBold = function(text) {
1131
-
1132
- // <strong> must go first:
1133
- text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
1134
- "<strong>$2</strong>");
1135
-
1136
- text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
1137
- "<em>$2</em>");
1138
-
1139
- return text;
1140
- }
1141
-
1142
-
1143
- var _DoBlockQuotes = function(text) {
1144
-
1145
- /*
1146
- text = text.replace(/
1147
- ( // Wrap whole match in $1
1148
- (
1149
- ^[ \t]*>[ \t]? // '>' at the start of a line
1150
- .+\n // rest of the first line
1151
- (.+\n)* // subsequent consecutive lines
1152
- \n* // blanks
1153
- )+
1154
- )
1155
- /gm, function(){...});
1156
- */
1157
-
1158
- text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
1159
- function(wholeMatch,m1) {
1160
- var bq = m1;
1161
-
1162
- // attacklab: hack around Konqueror 3.5.4 bug:
1163
- // "----------bug".replace(/^-/g,"") == "bug"
1164
-
1165
- bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
1166
-
1167
- // attacklab: clean up hack
1168
- bq = bq.replace(/~0/g,"");
1169
-
1170
- bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
1171
- bq = _RunBlockGamut(bq); // recurse
1172
-
1173
- bq = bq.replace(/(^|\n)/g,"$1 ");
1174
- // These leading spaces screw with <pre> content, so we need to fix that:
1175
- bq = bq.replace(
1176
- /(\s*<pre>[^\r]+?<\/pre>)/gm,
1177
- function(wholeMatch,m1) {
1178
- var pre = m1;
1179
- // attacklab: hack around Konqueror 3.5.4 bug:
1180
- pre = pre.replace(/^ /mg,"~0");
1181
- pre = pre.replace(/~0/g,"");
1182
- return pre;
1183
- });
1184
-
1185
- return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
1186
- });
1187
- return text;
1188
- }
1189
-
1190
-
1191
- var _FormParagraphs = function(text) {
1192
- //
1193
- // Params:
1194
- // $text - string to process with html <p> tags
1195
- //
1196
-
1197
- // Strip leading and trailing lines:
1198
- text = text.replace(/^\n+/g,"");
1199
- text = text.replace(/\n+$/g,"");
1200
-
1201
- var grafs = text.split(/\n{2,}/g);
1202
- var grafsOut = [];
1203
-
1204
- //
1205
- // Wrap <p> tags.
1206
- //
1207
- var end = grafs.length;
1208
- for (var i=0; i<end; i++) {
1209
- var str = grafs[i];
1210
-
1211
- // if this is an HTML marker, copy it
1212
- if (str.search(/~K(\d+)K/g) >= 0) {
1213
- grafsOut.push(str);
1214
- }
1215
- else if (str.search(/\S/) >= 0) {
1216
- str = _RunSpanGamut(str);
1217
- str = str.replace(/^([ \t]*)/g,"<p>");
1218
- str += "</p>"
1219
- grafsOut.push(str);
1220
- }
1221
-
1222
- }
1223
-
1224
- //
1225
- // Unhashify HTML blocks
1226
- //
1227
- end = grafsOut.length;
1228
- for (var i=0; i<end; i++) {
1229
- // if this is a marker for an html block...
1230
- while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
1231
- var blockText = g_html_blocks[RegExp.$1];
1232
- blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
1233
- grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
1234
- }
1235
- }
1236
-
1237
- return grafsOut.join("\n\n");
1238
- }
1239
-
1240
-
1241
- var _EncodeAmpsAndAngles = function(text) {
1242
- // Smart processing for ampersands and angle brackets that need to be encoded.
1243
-
1244
- // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1245
- // http://bumppo.net/projects/amputator/
1246
- text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
1247
-
1248
- // Encode naked <'s
1249
- text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
1250
-
1251
- return text;
1252
- }
1253
-
1254
-
1255
- var _EncodeBackslashEscapes = function(text) {
1256
- //
1257
- // Parameter: String.
1258
- // Returns: The string, with after processing the following backslash
1259
- // escape sequences.
1260
- //
1261
-
1262
- // attacklab: The polite way to do this is with the new
1263
- // escapeCharacters() function:
1264
- //
1265
- // text = escapeCharacters(text,"\\",true);
1266
- // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1267
- //
1268
- // ...but we're sidestepping its use of the (slow) RegExp constructor
1269
- // as an optimization for Firefox. This function gets called a LOT.
1270
-
1271
- text = text.replace(/\\(\\)/g,escapeCharacters_callback);
1272
- text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
1273
- return text;
1274
- }
1275
-
1276
-
1277
- var _DoAutoLinks = function(text) {
293
+ /**
294
+ * Gets or registers an extension
295
+ * @static
296
+ * @param {string} name
297
+ * @param {object|function=} ext
298
+ * @returns {*}
299
+ */
300
+ showdown.extension = function (name, ext) {
301
+ 'use strict';
302
+
303
+ if (!showdown.helper.isString(name)) {
304
+ throw Error('Extension \'name\' must be a string');
305
+ }
306
+
307
+ name = showdown.helper.stdExtName(name);
308
+
309
+ // Getter
310
+ if (showdown.helper.isUndefined(ext)) {
311
+ if (!extensions.hasOwnProperty(name)) {
312
+ throw Error('Extension named ' + name + ' is not registered!');
313
+ }
314
+ return extensions[name];
315
+
316
+ // Setter
317
+ } else {
318
+ // Expand extension if it's wrapped in a function
319
+ if (typeof ext === 'function') {
320
+ ext = ext();
321
+ }
322
+
323
+ // Ensure extension is an array
324
+ if (!showdown.helper.isArray(ext)) {
325
+ ext = [ext];
326
+ }
327
+
328
+ var validExtension = validate(ext, name);
329
+
330
+ if (validExtension.valid) {
331
+ extensions[name] = ext;
332
+ } else {
333
+ throw Error(validExtension.error);
334
+ }
335
+ }
336
+ };
1278
337
 
1279
- text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
338
+ /**
339
+ * Gets all extensions registered
340
+ * @returns {{}}
341
+ */
342
+ showdown.getAllExtensions = function () {
343
+ 'use strict';
344
+ return extensions;
345
+ };
1280
346
 
1281
- // Email addresses: <address@domain.foo>
347
+ /**
348
+ * Remove an extension
349
+ * @param {string} name
350
+ */
351
+ showdown.removeExtension = function (name) {
352
+ 'use strict';
353
+ delete extensions[name];
354
+ };
1282
355
 
1283
- /*
1284
- text = text.replace(/
1285
- <
1286
- (?:mailto:)?
1287
- (
1288
- [-.\w]+
1289
- \@
1290
- [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1291
- )
1292
- >
1293
- /gi, _DoAutoLinks_callback());
1294
- */
1295
- text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
1296
- function(wholeMatch,m1) {
1297
- return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
1298
- }
1299
- );
356
+ /**
357
+ * Removes all extensions
358
+ */
359
+ showdown.resetExtensions = function () {
360
+ 'use strict';
361
+ extensions = {};
362
+ };
1300
363
 
1301
- return text;
364
+ /**
365
+ * Validate extension
366
+ * @param {array} extension
367
+ * @param {string} name
368
+ * @returns {{valid: boolean, error: string}}
369
+ */
370
+ function validate(extension, name) {
371
+ 'use strict';
372
+
373
+ var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
374
+ ret = {
375
+ valid: true,
376
+ error: ''
377
+ };
378
+
379
+ if (!showdown.helper.isArray(extension)) {
380
+ extension = [extension];
381
+ }
382
+
383
+ for (var i = 0; i < extension.length; ++i) {
384
+ var baseMsg = errMsg + ' sub-extension ' + i + ': ',
385
+ ext = extension[i];
386
+ if (typeof ext !== 'object') {
387
+ ret.valid = false;
388
+ ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
389
+ return ret;
390
+ }
391
+
392
+ if (!showdown.helper.isString(ext.type)) {
393
+ ret.valid = false;
394
+ ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
395
+ return ret;
396
+ }
397
+
398
+ var type = ext.type = ext.type.toLowerCase();
399
+
400
+ // normalize extension type
401
+ if (type === 'language') {
402
+ type = ext.type = 'lang';
403
+ }
404
+
405
+ if (type === 'html') {
406
+ type = ext.type = 'output';
407
+ }
408
+
409
+ if (type !== 'lang' && type !== 'output' && type !== 'listener') {
410
+ ret.valid = false;
411
+ ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"';
412
+ return ret;
413
+ }
414
+
415
+ if (type === 'listener') {
416
+ if (showdown.helper.isUndefined(ext.listeners)) {
417
+ ret.valid = false;
418
+ ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"';
419
+ return ret;
420
+ }
421
+ } else {
422
+ if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
423
+ ret.valid = false;
424
+ ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method';
425
+ return ret;
426
+ }
427
+ }
428
+
429
+ if (ext.listeners) {
430
+ if (typeof ext.listeners !== 'object') {
431
+ ret.valid = false;
432
+ ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given';
433
+ return ret;
434
+ }
435
+ for (var ln in ext.listeners) {
436
+ if (ext.listeners.hasOwnProperty(ln)) {
437
+ if (typeof ext.listeners[ln] !== 'function') {
438
+ ret.valid = false;
439
+ ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln +
440
+ ' must be a function but ' + typeof ext.listeners[ln] + ' given';
441
+ return ret;
442
+ }
443
+ }
444
+ }
445
+ }
446
+
447
+ if (ext.filter) {
448
+ if (typeof ext.filter !== 'function') {
449
+ ret.valid = false;
450
+ ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
451
+ return ret;
452
+ }
453
+ } else if (ext.regex) {
454
+ if (showdown.helper.isString(ext.regex)) {
455
+ ext.regex = new RegExp(ext.regex, 'g');
456
+ }
457
+ if (!ext.regex instanceof RegExp) {
458
+ ret.valid = false;
459
+ ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given';
460
+ return ret;
461
+ }
462
+ if (showdown.helper.isUndefined(ext.replace)) {
463
+ ret.valid = false;
464
+ ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
465
+ return ret;
466
+ }
467
+ }
468
+ }
469
+ return ret;
1302
470
  }
1303
471
 
472
+ /**
473
+ * Validate extension
474
+ * @param {object} ext
475
+ * @returns {boolean}
476
+ */
477
+ showdown.validateExtension = function (ext) {
478
+ 'use strict';
479
+
480
+ var validateExtension = validate(ext, null);
481
+ if (!validateExtension.valid) {
482
+ console.warn(validateExtension.error);
483
+ return false;
484
+ }
485
+ return true;
486
+ };
1304
487
 
1305
- var _EncodeEmailAddress = function(addr) {
1306
- //
1307
- // Input: an email address, e.g. "foo@example.com"
1308
- //
1309
- // Output: the email address as a mailto link, with each character
1310
- // of the address encoded as either a decimal or hex entity, in
1311
- // the hopes of foiling most address harvesting spam bots. E.g.:
1312
- //
1313
- // <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1314
- // x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1315
- // &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1316
- //
1317
- // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1318
- // mailing list: <http://tinyurl.com/yu7ue>
1319
- //
488
+ /**
489
+ * showdownjs helper functions
490
+ */
1320
491
 
1321
- var encode = [
1322
- function(ch){return "&#"+ch.charCodeAt(0)+";";},
1323
- function(ch){return "&#x"+ch.charCodeAt(0).toString(16)+";";},
1324
- function(ch){return ch;}
1325
- ];
1326
-
1327
- addr = "mailto:" + addr;
1328
-
1329
- addr = addr.replace(/./g, function(ch) {
1330
- if (ch == "@") {
1331
- // this *must* be encoded. I insist.
1332
- ch = encode[Math.floor(Math.random()*2)](ch);
1333
- } else if (ch !=":") {
1334
- // leave ':' alone (to spot mailto: later)
1335
- var r = Math.random();
1336
- // roughly 10% raw, 45% hex, 45% dec
1337
- ch = (
1338
- r > .9 ? encode[2](ch) :
1339
- r > .45 ? encode[1](ch) :
1340
- encode[0](ch)
1341
- );
1342
- }
1343
- return ch;
1344
- });
1345
-
1346
- addr = "<a href=\"" + addr + "\">" + addr + "</a>";
1347
- addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
1348
-
1349
- return addr;
492
+ if (!showdown.hasOwnProperty('helper')) {
493
+ showdown.helper = {};
1350
494
  }
1351
495
 
496
+ /**
497
+ * Check if var is string
498
+ * @static
499
+ * @param {string} a
500
+ * @returns {boolean}
501
+ */
502
+ showdown.helper.isString = function isString(a) {
503
+ 'use strict';
504
+ return (typeof a === 'string' || a instanceof String);
505
+ };
1352
506
 
1353
- var _UnescapeSpecialChars = function(text) {
1354
- //
1355
- // Swap back in all the special characters we've hidden.
1356
- //
1357
- text = text.replace(/~E(\d+)E/g,
1358
- function(wholeMatch,m1) {
1359
- var charCodeToReplace = parseInt(m1);
1360
- return String.fromCharCode(charCodeToReplace);
1361
- }
1362
- );
1363
- return text;
1364
- }
1365
-
507
+ /**
508
+ * Check if var is a function
509
+ * @static
510
+ * @param {string} a
511
+ * @returns {boolean}
512
+ */
513
+ showdown.helper.isFunction = function isFunction(a) {
514
+ 'use strict';
515
+ var getType = {};
516
+ return a && getType.toString.call(a) === '[object Function]';
517
+ };
1366
518
 
1367
- var _Outdent = function(text) {
1368
- //
1369
- // Remove one level of line-leading tabs or spaces
1370
- //
519
+ /**
520
+ * ForEach helper function
521
+ * @static
522
+ * @param {*} obj
523
+ * @param {function} callback
524
+ */
525
+ showdown.helper.forEach = function forEach(obj, callback) {
526
+ 'use strict';
527
+ if (typeof obj.forEach === 'function') {
528
+ obj.forEach(callback);
529
+ } else {
530
+ for (var i = 0; i < obj.length; i++) {
531
+ callback(obj[i], i, obj);
532
+ }
533
+ }
534
+ };
1371
535
 
1372
- // attacklab: hack around Konqueror 3.5.4 bug:
1373
- // "----------bug".replace(/^-/g,"") == "bug"
536
+ /**
537
+ * isArray helper function
538
+ * @static
539
+ * @param {*} a
540
+ * @returns {boolean}
541
+ */
542
+ showdown.helper.isArray = function isArray(a) {
543
+ 'use strict';
544
+ return a.constructor === Array;
545
+ };
1374
546
 
1375
- text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
547
+ /**
548
+ * Check if value is undefined
549
+ * @static
550
+ * @param {*} value The value to check.
551
+ * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
552
+ */
553
+ showdown.helper.isUndefined = function isUndefined(value) {
554
+ 'use strict';
555
+ return typeof value === 'undefined';
556
+ };
1376
557
 
1377
- // attacklab: clean up hack
1378
- text = text.replace(/~0/g,"")
558
+ /**
559
+ * Standardidize extension name
560
+ * @static
561
+ * @param {string} s extension name
562
+ * @returns {string}
563
+ */
564
+ showdown.helper.stdExtName = function (s) {
565
+ 'use strict';
566
+ return s.replace(/[_-]||\s/g, '').toLowerCase();
567
+ };
1379
568
 
1380
- return text;
569
+ function escapeCharactersCallback(wholeMatch, m1) {
570
+ 'use strict';
571
+ var charCodeToEscape = m1.charCodeAt(0);
572
+ return '~E' + charCodeToEscape + 'E';
1381
573
  }
1382
574
 
1383
- var _Detab = function(text) {
1384
- // attacklab: Detab's completely rewritten for speed.
1385
- // In perl we could fix it by anchoring the regexp with \G.
1386
- // In javascript we're less fortunate.
1387
-
1388
- // expand first n-1 tabs
1389
- text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
1390
-
1391
- // replace the nth with two sentinels
1392
- text = text.replace(/\t/g,"~A~B");
1393
-
1394
- // use the sentinel to anchor our regex so it doesn't explode
1395
- text = text.replace(/~B(.+?)~A/g,
1396
- function(wholeMatch,m1,m2) {
1397
- var leadingText = m1;
1398
- var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
575
+ /**
576
+ * Callback used to escape characters when passing through String.replace
577
+ * @static
578
+ * @param {string} wholeMatch
579
+ * @param {string} m1
580
+ * @returns {string}
581
+ */
582
+ showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
583
+
584
+ /**
585
+ * Escape characters in a string
586
+ * @static
587
+ * @param {string} text
588
+ * @param {string} charsToEscape
589
+ * @param {boolean} afterBackslash
590
+ * @returns {XML|string|void|*}
591
+ */
592
+ showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) {
593
+ 'use strict';
594
+ // First we have to escape the escape characters so that
595
+ // we can build a character class out of them
596
+ var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
597
+
598
+ if (afterBackslash) {
599
+ regexString = '\\\\' + regexString;
600
+ }
601
+
602
+ var regex = new RegExp(regexString, 'g');
603
+ text = text.replace(regex, escapeCharactersCallback);
604
+
605
+ return text;
606
+ };
1399
607
 
1400
- // there *must* be a better way to do this:
1401
- for (var i=0; i<numSpaces; i++) leadingText+=" ";
608
+ var rgxFindMatchPos = function (str, left, right, flags) {
609
+ 'use strict';
610
+ var f = flags || '',
611
+ g = f.indexOf('g') > -1,
612
+ x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')),
613
+ l = new RegExp(left, f.replace(/g/g, '')),
614
+ pos = [],
615
+ t, s, m, start, end;
616
+
617
+ do {
618
+ t = 0;
619
+ while ((m = x.exec(str))) {
620
+ if (l.test(m[0])) {
621
+ if (!(t++)) {
622
+ s = x.lastIndex;
623
+ start = s - m[0].length;
624
+ }
625
+ } else if (t) {
626
+ if (!--t) {
627
+ end = m.index + m[0].length;
628
+ var obj = {
629
+ left: {start: start, end: s},
630
+ match: {start: s, end: m.index},
631
+ right: {start: m.index, end: end},
632
+ wholeMatch: {start: start, end: end}
633
+ };
634
+ pos.push(obj);
635
+ if (!g) {
636
+ return pos;
637
+ }
638
+ }
639
+ }
640
+ }
641
+ } while (t && (x.lastIndex = s));
642
+
643
+ return pos;
644
+ };
1402
645
 
1403
- return leadingText;
1404
- }
1405
- );
646
+ /**
647
+ * matchRecursiveRegExp
648
+ *
649
+ * (c) 2007 Steven Levithan <stevenlevithan.com>
650
+ * MIT License
651
+ *
652
+ * Accepts a string to search, a left and right format delimiter
653
+ * as regex patterns, and optional regex flags. Returns an array
654
+ * of matches, allowing nested instances of left/right delimiters.
655
+ * Use the "g" flag to return all matches, otherwise only the
656
+ * first is returned. Be careful to ensure that the left and
657
+ * right format delimiters produce mutually exclusive matches.
658
+ * Backreferences are not supported within the right delimiter
659
+ * due to how it is internally combined with the left delimiter.
660
+ * When matching strings whose format delimiters are unbalanced
661
+ * to the left or right, the output is intentionally as a
662
+ * conventional regex library with recursion support would
663
+ * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
664
+ * "<" and ">" as the delimiters (both strings contain a single,
665
+ * balanced instance of "<x>").
666
+ *
667
+ * examples:
668
+ * matchRecursiveRegExp("test", "\\(", "\\)")
669
+ * returns: []
670
+ * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
671
+ * returns: ["t<<e>><s>", ""]
672
+ * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
673
+ * returns: ["test"]
674
+ */
675
+ showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
676
+ 'use strict';
677
+
678
+ var matchPos = rgxFindMatchPos (str, left, right, flags),
679
+ results = [];
680
+
681
+ for (var i = 0; i < matchPos.length; ++i) {
682
+ results.push([
683
+ str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
684
+ str.slice(matchPos[i].match.start, matchPos[i].match.end),
685
+ str.slice(matchPos[i].left.start, matchPos[i].left.end),
686
+ str.slice(matchPos[i].right.start, matchPos[i].right.end)
687
+ ]);
688
+ }
689
+ return results;
690
+ };
1406
691
 
1407
- // clean up sentinels
1408
- text = text.replace(/~A/g," "); // attacklab: g_tab_width
1409
- text = text.replace(/~B/g,"");
692
+ /**
693
+ *
694
+ * @param {string} str
695
+ * @param {string|function} replacement
696
+ * @param {string} left
697
+ * @param {string} right
698
+ * @param {string} flags
699
+ * @returns {string}
700
+ */
701
+ showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
702
+ 'use strict';
703
+
704
+ if (!showdown.helper.isFunction(replacement)) {
705
+ var repStr = replacement;
706
+ replacement = function () {
707
+ return repStr;
708
+ };
709
+ }
710
+
711
+ var matchPos = rgxFindMatchPos(str, left, right, flags),
712
+ finalStr = str,
713
+ lng = matchPos.length;
714
+
715
+ if (lng > 0) {
716
+ var bits = [];
717
+ if (matchPos[0].wholeMatch.start !== 0) {
718
+ bits.push(str.slice(0, matchPos[0].wholeMatch.start));
719
+ }
720
+ for (var i = 0; i < lng; ++i) {
721
+ bits.push(
722
+ replacement(
723
+ str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
724
+ str.slice(matchPos[i].match.start, matchPos[i].match.end),
725
+ str.slice(matchPos[i].left.start, matchPos[i].left.end),
726
+ str.slice(matchPos[i].right.start, matchPos[i].right.end)
727
+ )
728
+ );
729
+ if (i < lng - 1) {
730
+ bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
731
+ }
732
+ }
733
+ if (matchPos[lng - 1].wholeMatch.end < str.length) {
734
+ bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
735
+ }
736
+ finalStr = bits.join('');
737
+ }
738
+ return finalStr;
739
+ };
1410
740
 
1411
- return text;
741
+ /**
742
+ * POLYFILLS
743
+ */
744
+ if (showdown.helper.isUndefined(console)) {
745
+ console = {
746
+ warn: function (msg) {
747
+ 'use strict';
748
+ alert(msg);
749
+ },
750
+ log: function (msg) {
751
+ 'use strict';
752
+ alert(msg);
753
+ },
754
+ error: function (msg) {
755
+ 'use strict';
756
+ throw msg;
757
+ }
758
+ };
1412
759
  }
1413
760
 
761
+ /**
762
+ * Created by Estevao on 31-05-2015.
763
+ */
764
+
765
+ /**
766
+ * Showdown Converter class
767
+ * @class
768
+ * @param {object} [converterOptions]
769
+ * @returns {Converter}
770
+ */
771
+ showdown.Converter = function (converterOptions) {
772
+ 'use strict';
773
+
774
+ var
775
+ /**
776
+ * Options used by this converter
777
+ * @private
778
+ * @type {{}}
779
+ */
780
+ options = {},
781
+
782
+ /**
783
+ * Language extensions used by this converter
784
+ * @private
785
+ * @type {Array}
786
+ */
787
+ langExtensions = [],
788
+
789
+ /**
790
+ * Output modifiers extensions used by this converter
791
+ * @private
792
+ * @type {Array}
793
+ */
794
+ outputModifiers = [],
795
+
796
+ /**
797
+ * Event listeners
798
+ * @private
799
+ * @type {{}}
800
+ */
801
+ listeners = {};
802
+
803
+ _constructor();
804
+
805
+ /**
806
+ * Converter constructor
807
+ * @private
808
+ */
809
+ function _constructor() {
810
+ converterOptions = converterOptions || {};
811
+
812
+ for (var gOpt in globalOptions) {
813
+ if (globalOptions.hasOwnProperty(gOpt)) {
814
+ options[gOpt] = globalOptions[gOpt];
815
+ }
816
+ }
817
+
818
+ // Merge options
819
+ if (typeof converterOptions === 'object') {
820
+ for (var opt in converterOptions) {
821
+ if (converterOptions.hasOwnProperty(opt)) {
822
+ options[opt] = converterOptions[opt];
823
+ }
824
+ }
825
+ } else {
826
+ throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions +
827
+ ' was passed instead.');
828
+ }
829
+
830
+ if (options.extensions) {
831
+ showdown.helper.forEach(options.extensions, _parseExtension);
832
+ }
833
+ }
834
+
835
+ /**
836
+ * Parse extension
837
+ * @param {*} ext
838
+ * @param {string} [name='']
839
+ * @private
840
+ */
841
+ function _parseExtension(ext, name) {
842
+
843
+ name = name || null;
844
+ // If it's a string, the extension was previously loaded
845
+ if (showdown.helper.isString(ext)) {
846
+ ext = showdown.helper.stdExtName(ext);
847
+ name = ext;
848
+
849
+ // LEGACY_SUPPORT CODE
850
+ if (showdown.extensions[ext]) {
851
+ console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
852
+ 'Please inform the developer that the extension should be updated!');
853
+ legacyExtensionLoading(showdown.extensions[ext], ext);
854
+ return;
855
+ // END LEGACY SUPPORT CODE
856
+
857
+ } else if (!showdown.helper.isUndefined(extensions[ext])) {
858
+ ext = extensions[ext];
859
+
860
+ } else {
861
+ throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
862
+ }
863
+ }
864
+
865
+ if (typeof ext === 'function') {
866
+ ext = ext();
867
+ }
868
+
869
+ if (!showdown.helper.isArray(ext)) {
870
+ ext = [ext];
871
+ }
872
+
873
+ var validExt = validate(ext, name);
874
+ if (!validExt.valid) {
875
+ throw Error(validExt.error);
876
+ }
877
+
878
+ for (var i = 0; i < ext.length; ++i) {
879
+ switch (ext[i].type) {
880
+
881
+ case 'lang':
882
+ langExtensions.push(ext[i]);
883
+ break;
884
+
885
+ case 'output':
886
+ outputModifiers.push(ext[i]);
887
+ break;
888
+ }
889
+ if (ext[i].hasOwnProperty(listeners)) {
890
+ for (var ln in ext[i].listeners) {
891
+ if (ext[i].listeners.hasOwnProperty(ln)) {
892
+ listen(ln, ext[i].listeners[ln]);
893
+ }
894
+ }
895
+ }
896
+ }
897
+
898
+ }
899
+
900
+ /**
901
+ * LEGACY_SUPPORT
902
+ * @param {*} ext
903
+ * @param {string} name
904
+ */
905
+ function legacyExtensionLoading(ext, name) {
906
+ if (typeof ext === 'function') {
907
+ ext = ext(new showdown.Converter());
908
+ }
909
+ if (!showdown.helper.isArray(ext)) {
910
+ ext = [ext];
911
+ }
912
+ var valid = validate(ext, name);
913
+
914
+ if (!valid.valid) {
915
+ throw Error(valid.error);
916
+ }
917
+
918
+ for (var i = 0; i < ext.length; ++i) {
919
+ switch (ext[i].type) {
920
+ case 'lang':
921
+ langExtensions.push(ext[i]);
922
+ break;
923
+ case 'output':
924
+ outputModifiers.push(ext[i]);
925
+ break;
926
+ default:// should never reach here
927
+ throw Error('Extension loader error: Type unrecognized!!!');
928
+ }
929
+ }
930
+ }
931
+
932
+ /**
933
+ * Listen to an event
934
+ * @param {string} name
935
+ * @param {function} callback
936
+ */
937
+ function listen(name, callback) {
938
+ if (!showdown.helper.isString(name)) {
939
+ throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given');
940
+ }
941
+
942
+ if (typeof callback !== 'function') {
943
+ throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given');
944
+ }
945
+
946
+ if (!listeners.hasOwnProperty(name)) {
947
+ listeners[name] = [];
948
+ }
949
+ listeners[name].push(callback);
950
+ }
951
+
952
+ function rTrimInputText(text) {
953
+ var rsp = text.match(/^\s*/)[0].length,
954
+ rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm');
955
+ return text.replace(rgx, '');
956
+ }
957
+
958
+ /**
959
+ * Dispatch an event
960
+ * @private
961
+ * @param {string} evtName Event name
962
+ * @param {string} text Text
963
+ * @param {{}} options Converter Options
964
+ * @param {{}} globals
965
+ * @returns {string}
966
+ */
967
+ this._dispatch = function dispatch (evtName, text, options, globals) {
968
+ if (listeners.hasOwnProperty(evtName)) {
969
+ for (var ei = 0; ei < listeners[evtName].length; ++ei) {
970
+ var nText = listeners[evtName][ei](evtName, text, this, options, globals);
971
+ if (nText && typeof nText !== 'undefined') {
972
+ text = nText;
973
+ }
974
+ }
975
+ }
976
+ return text;
977
+ };
978
+
979
+ /**
980
+ * Listen to an event
981
+ * @param {string} name
982
+ * @param {function} callback
983
+ * @returns {showdown.Converter}
984
+ */
985
+ this.listen = function (name, callback) {
986
+ listen(name, callback);
987
+ return this;
988
+ };
989
+
990
+ /**
991
+ * Converts a markdown string into HTML
992
+ * @param {string} text
993
+ * @returns {*}
994
+ */
995
+ this.makeHtml = function (text) {
996
+ //check if text is not falsy
997
+ if (!text) {
998
+ return text;
999
+ }
1000
+
1001
+ var globals = {
1002
+ gHtmlBlocks: [],
1003
+ gHtmlMdBlocks: [],
1004
+ gHtmlSpans: [],
1005
+ gUrls: {},
1006
+ gTitles: {},
1007
+ gDimensions: {},
1008
+ gListLevel: 0,
1009
+ hashLinkCounts: {},
1010
+ langExtensions: langExtensions,
1011
+ outputModifiers: outputModifiers,
1012
+ converter: this,
1013
+ ghCodeBlocks: []
1014
+ };
1015
+
1016
+ // attacklab: Replace ~ with ~T
1017
+ // This lets us use tilde as an escape char to avoid md5 hashes
1018
+ // The choice of character is arbitrary; anything that isn't
1019
+ // magic in Markdown will work.
1020
+ text = text.replace(/~/g, '~T');
1021
+
1022
+ // attacklab: Replace $ with ~D
1023
+ // RegExp interprets $ as a special character
1024
+ // when it's in a replacement string
1025
+ text = text.replace(/\$/g, '~D');
1026
+
1027
+ // Standardize line endings
1028
+ text = text.replace(/\r\n/g, '\n'); // DOS to Unix
1029
+ text = text.replace(/\r/g, '\n'); // Mac to Unix
1030
+
1031
+ if (options.smartIndentationFix) {
1032
+ text = rTrimInputText(text);
1033
+ }
1034
+
1035
+ // Make sure text begins and ends with a couple of newlines:
1036
+ text = '\n\n' + text + '\n\n';
1037
+
1038
+ // detab
1039
+ text = showdown.subParser('detab')(text, options, globals);
1040
+
1041
+ // stripBlankLines
1042
+ text = showdown.subParser('stripBlankLines')(text, options, globals);
1043
+
1044
+ //run languageExtensions
1045
+ showdown.helper.forEach(langExtensions, function (ext) {
1046
+ text = showdown.subParser('runExtension')(ext, text, options, globals);
1047
+ });
1414
1048
 
1415
- //
1416
- // attacklab: Utility functions
1417
- //
1418
-
1419
-
1420
- var escapeCharacters = function(text, charsToEscape, afterBackslash) {
1421
- // First we have to escape the escape characters so that
1422
- // we can build a character class out of them
1423
- var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";
1049
+ // run the sub parsers
1050
+ text = showdown.subParser('hashPreCodeTags')(text, options, globals);
1051
+ text = showdown.subParser('githubCodeBlocks')(text, options, globals);
1052
+ text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
1053
+ text = showdown.subParser('hashHTMLSpans')(text, options, globals);
1054
+ text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
1055
+ text = showdown.subParser('blockGamut')(text, options, globals);
1056
+ text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
1057
+ text = showdown.subParser('unescapeSpecialChars')(text, options, globals);
1058
+
1059
+ // attacklab: Restore dollar signs
1060
+ text = text.replace(/~D/g, '$$');
1061
+
1062
+ // attacklab: Restore tildes
1063
+ text = text.replace(/~T/g, '~');
1064
+
1065
+ // Run output modifiers
1066
+ showdown.helper.forEach(outputModifiers, function (ext) {
1067
+ text = showdown.subParser('runExtension')(ext, text, options, globals);
1068
+ });
1424
1069
 
1425
- if (afterBackslash) {
1426
- regexString = "\\\\" + regexString;
1427
- }
1070
+ return text;
1071
+ };
1072
+
1073
+ /**
1074
+ * Set an option of this Converter instance
1075
+ * @param {string} key
1076
+ * @param {*} value
1077
+ */
1078
+ this.setOption = function (key, value) {
1079
+ options[key] = value;
1080
+ };
1081
+
1082
+ /**
1083
+ * Get the option of this Converter instance
1084
+ * @param {string} key
1085
+ * @returns {*}
1086
+ */
1087
+ this.getOption = function (key) {
1088
+ return options[key];
1089
+ };
1090
+
1091
+ /**
1092
+ * Get the options of this Converter instance
1093
+ * @returns {{}}
1094
+ */
1095
+ this.getOptions = function () {
1096
+ return options;
1097
+ };
1098
+
1099
+ /**
1100
+ * Add extension to THIS converter
1101
+ * @param {{}} extension
1102
+ * @param {string} [name=null]
1103
+ */
1104
+ this.addExtension = function (extension, name) {
1105
+ name = name || null;
1106
+ _parseExtension(extension, name);
1107
+ };
1108
+
1109
+ /**
1110
+ * Use a global registered extension with THIS converter
1111
+ * @param {string} extensionName Name of the previously registered extension
1112
+ */
1113
+ this.useExtension = function (extensionName) {
1114
+ _parseExtension(extensionName);
1115
+ };
1116
+
1117
+ /**
1118
+ * Set the flavor THIS converter should use
1119
+ * @param {string} name
1120
+ */
1121
+ this.setFlavor = function (name) {
1122
+ if (flavor.hasOwnProperty(name)) {
1123
+ var preset = flavor[name];
1124
+ for (var option in preset) {
1125
+ if (preset.hasOwnProperty(option)) {
1126
+ options[option] = preset[option];
1127
+ }
1128
+ }
1129
+ }
1130
+ };
1131
+
1132
+ /**
1133
+ * Remove an extension from THIS converter.
1134
+ * Note: This is a costly operation. It's better to initialize a new converter
1135
+ * and specify the extensions you wish to use
1136
+ * @param {Array} extension
1137
+ */
1138
+ this.removeExtension = function (extension) {
1139
+ if (!showdown.helper.isArray(extension)) {
1140
+ extension = [extension];
1141
+ }
1142
+ for (var a = 0; a < extension.length; ++a) {
1143
+ var ext = extension[a];
1144
+ for (var i = 0; i < langExtensions.length; ++i) {
1145
+ if (langExtensions[i] === ext) {
1146
+ langExtensions[i].splice(i, 1);
1147
+ }
1148
+ }
1149
+ for (var ii = 0; ii < outputModifiers.length; ++i) {
1150
+ if (outputModifiers[ii] === ext) {
1151
+ outputModifiers[ii].splice(i, 1);
1152
+ }
1153
+ }
1154
+ }
1155
+ };
1156
+
1157
+ /**
1158
+ * Get all extension of THIS converter
1159
+ * @returns {{language: Array, output: Array}}
1160
+ */
1161
+ this.getAllExtensions = function () {
1162
+ return {
1163
+ language: langExtensions,
1164
+ output: outputModifiers
1165
+ };
1166
+ };
1167
+ };
1428
1168
 
1429
- var regex = new RegExp(regexString,"g");
1430
- text = text.replace(regex,escapeCharacters_callback);
1169
+ /**
1170
+ * Turn Markdown link shortcuts into XHTML <a> tags.
1171
+ */
1172
+ showdown.subParser('anchors', function (text, options, globals) {
1173
+ 'use strict';
1174
+
1175
+ text = globals.converter._dispatch('anchors.before', text, options, globals);
1176
+
1177
+ var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
1178
+ if (showdown.helper.isUndefined(m7)) {
1179
+ m7 = '';
1180
+ }
1181
+ wholeMatch = m1;
1182
+ var linkText = m2,
1183
+ linkId = m3.toLowerCase(),
1184
+ url = m4,
1185
+ title = m7;
1186
+
1187
+ if (!url) {
1188
+ if (!linkId) {
1189
+ // lower-case and turn embedded newlines into spaces
1190
+ linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
1191
+ }
1192
+ url = '#' + linkId;
1193
+
1194
+ if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
1195
+ url = globals.gUrls[linkId];
1196
+ if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
1197
+ title = globals.gTitles[linkId];
1198
+ }
1199
+ } else {
1200
+ if (wholeMatch.search(/\(\s*\)$/m) > -1) {
1201
+ // Special case for explicit empty url
1202
+ url = '';
1203
+ } else {
1204
+ return wholeMatch;
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+ url = showdown.helper.escapeCharacters(url, '*_', false);
1210
+ var result = '<a href="' + url + '"';
1211
+
1212
+ if (title !== '' && title !== null) {
1213
+ title = title.replace(/"/g, '&quot;');
1214
+ title = showdown.helper.escapeCharacters(title, '*_', false);
1215
+ result += ' title="' + title + '"';
1216
+ }
1217
+
1218
+ result += '>' + linkText + '</a>';
1219
+
1220
+ return result;
1221
+ };
1222
+
1223
+ // First, handle reference-style links: [link text] [id]
1224
+ text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag);
1225
+
1226
+ // Next, inline-style links: [link text](url "optional title")
1227
+ text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,
1228
+ writeAnchorTag);
1229
+
1230
+ // Last, handle reference-style shortcuts: [link text]
1231
+ // These must come last in case you've also got [link test][1]
1232
+ // or [link test](/foo)
1233
+ text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag);
1234
+
1235
+ text = globals.converter._dispatch('anchors.after', text, options, globals);
1236
+ return text;
1237
+ });
1238
+
1239
+ showdown.subParser('autoLinks', function (text, options, globals) {
1240
+ 'use strict';
1241
+
1242
+ text = globals.converter._dispatch('autoLinks.before', text, options, globals);
1243
+
1244
+ var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi,
1245
+ delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
1246
+ simpleMailRegex = /(?:^|\s)([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|\s)/gi,
1247
+ delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
1248
+
1249
+ text = text.replace(delimUrlRegex, replaceLink);
1250
+ text = text.replace(delimMailRegex, replaceMail);
1251
+ // simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
1252
+ // Email addresses: <address@domain.foo>
1253
+
1254
+ if (options.simplifiedAutoLink) {
1255
+ text = text.replace(simpleURLRegex, replaceLink);
1256
+ text = text.replace(simpleMailRegex, replaceMail);
1257
+ }
1258
+
1259
+ function replaceLink(wm, link) {
1260
+ var lnkTxt = link;
1261
+ if (/^www\./i.test(link)) {
1262
+ link = link.replace(/^www\./i, 'http://www.');
1263
+ }
1264
+ return '<a href="' + link + '">' + lnkTxt + '</a>';
1265
+ }
1266
+
1267
+ function replaceMail(wholeMatch, m1) {
1268
+ var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1);
1269
+ return showdown.subParser('encodeEmailAddress')(unescapedStr);
1270
+ }
1271
+
1272
+ text = globals.converter._dispatch('autoLinks.after', text, options, globals);
1273
+
1274
+ return text;
1275
+ });
1276
+
1277
+ /**
1278
+ * These are all the transformations that form block-level
1279
+ * tags like paragraphs, headers, and list items.
1280
+ */
1281
+ showdown.subParser('blockGamut', function (text, options, globals) {
1282
+ 'use strict';
1283
+
1284
+ text = globals.converter._dispatch('blockGamut.before', text, options, globals);
1285
+
1286
+ // we parse blockquotes first so that we can have headings and hrs
1287
+ // inside blockquotes
1288
+ text = showdown.subParser('blockQuotes')(text, options, globals);
1289
+ text = showdown.subParser('headers')(text, options, globals);
1290
+
1291
+ // Do Horizontal Rules:
1292
+ var key = showdown.subParser('hashBlock')('<hr />', options, globals);
1293
+ text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key);
1294
+ text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
1295
+ text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
1296
+
1297
+ text = showdown.subParser('lists')(text, options, globals);
1298
+ text = showdown.subParser('codeBlocks')(text, options, globals);
1299
+ text = showdown.subParser('tables')(text, options, globals);
1300
+
1301
+ // We already ran _HashHTMLBlocks() before, in Markdown(), but that
1302
+ // was to escape raw HTML in the original Markdown source. This time,
1303
+ // we're escaping the markup we've just created, so that we don't wrap
1304
+ // <p> tags around block-level tags.
1305
+ text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
1306
+ text = showdown.subParser('paragraphs')(text, options, globals);
1307
+
1308
+ text = globals.converter._dispatch('blockGamut.after', text, options, globals);
1309
+
1310
+ return text;
1311
+ });
1312
+
1313
+ showdown.subParser('blockQuotes', function (text, options, globals) {
1314
+ 'use strict';
1315
+
1316
+ text = globals.converter._dispatch('blockQuotes.before', text, options, globals);
1431
1317
 
1432
- return text;
1433
- }
1318
+ text = text.replace(/((^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
1319
+ var bq = m1;
1320
+
1321
+ // attacklab: hack around Konqueror 3.5.4 bug:
1322
+ // "----------bug".replace(/^-/g,"") == "bug"
1323
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting
1324
+
1325
+ // attacklab: clean up hack
1326
+ bq = bq.replace(/~0/g, '');
1327
+
1328
+ bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
1329
+ bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
1330
+ bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
1331
+
1332
+ bq = bq.replace(/(^|\n)/g, '$1 ');
1333
+ // These leading spaces screw with <pre> content, so we need to fix that:
1334
+ bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
1335
+ var pre = m1;
1336
+ // attacklab: hack around Konqueror 3.5.4 bug:
1337
+ pre = pre.replace(/^ /mg, '~0');
1338
+ pre = pre.replace(/~0/g, '');
1339
+ return pre;
1340
+ });
1434
1341
 
1342
+ return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
1343
+ });
1344
+
1345
+ text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
1346
+ return text;
1347
+ });
1348
+
1349
+ /**
1350
+ * Process Markdown `<pre><code>` blocks.
1351
+ */
1352
+ showdown.subParser('codeBlocks', function (text, options, globals) {
1353
+ 'use strict';
1354
+
1355
+ text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
1356
+
1357
+ // sentinel workarounds for lack of \A and \Z, safari\khtml bug
1358
+ text += '~0';
1359
+
1360
+ var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
1361
+ text = text.replace(pattern, function (wholeMatch, m1, m2) {
1362
+ var codeblock = m1,
1363
+ nextChar = m2,
1364
+ end = '\n';
1365
+
1366
+ codeblock = showdown.subParser('outdent')(codeblock);
1367
+ codeblock = showdown.subParser('encodeCode')(codeblock);
1368
+ codeblock = showdown.subParser('detab')(codeblock);
1369
+ codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
1370
+ codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
1371
+
1372
+ if (options.omitExtraWLInCodeBlocks) {
1373
+ end = '';
1374
+ }
1375
+
1376
+ codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
1377
+
1378
+ return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
1379
+ });
1380
+
1381
+ // strip sentinel
1382
+ text = text.replace(/~0/, '');
1383
+
1384
+ text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
1385
+ return text;
1386
+ });
1387
+
1388
+ /**
1389
+ *
1390
+ * * Backtick quotes are used for <code></code> spans.
1391
+ *
1392
+ * * You can use multiple backticks as the delimiters if you want to
1393
+ * include literal backticks in the code span. So, this input:
1394
+ *
1395
+ * Just type ``foo `bar` baz`` at the prompt.
1396
+ *
1397
+ * Will translate to:
1398
+ *
1399
+ * <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
1400
+ *
1401
+ * There's no arbitrary limit to the number of backticks you
1402
+ * can use as delimters. If you need three consecutive backticks
1403
+ * in your code, use four for delimiters, etc.
1404
+ *
1405
+ * * You can use spaces to get literal backticks at the edges:
1406
+ *
1407
+ * ... type `` `bar` `` ...
1408
+ *
1409
+ * Turns to:
1410
+ *
1411
+ * ... type <code>`bar`</code> ...
1412
+ */
1413
+ showdown.subParser('codeSpans', function (text, options, globals) {
1414
+ 'use strict';
1415
+
1416
+ text = globals.converter._dispatch('codeSpans.before', text, options, globals);
1417
+
1418
+ /*
1419
+ text = text.replace(/
1420
+ (^|[^\\]) // Character before opening ` can't be a backslash
1421
+ (`+) // $2 = Opening run of `
1422
+ ( // $3 = The code block
1423
+ [^\r]*?
1424
+ [^`] // attacklab: work around lack of lookbehind
1425
+ )
1426
+ \2 // Matching closer
1427
+ (?!`)
1428
+ /gm, function(){...});
1429
+ */
1430
+
1431
+ if (typeof(text) === 'undefined') {
1432
+ text = '';
1433
+ }
1434
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
1435
+ function (wholeMatch, m1, m2, m3) {
1436
+ var c = m3;
1437
+ c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
1438
+ c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
1439
+ c = showdown.subParser('encodeCode')(c);
1440
+ return m1 + '<code>' + c + '</code>';
1441
+ }
1442
+ );
1443
+
1444
+ text = globals.converter._dispatch('codeSpans.after', text, options, globals);
1445
+ return text;
1446
+ });
1447
+
1448
+ /**
1449
+ * Convert all tabs to spaces
1450
+ */
1451
+ showdown.subParser('detab', function (text) {
1452
+ 'use strict';
1453
+
1454
+ // expand first n-1 tabs
1455
+ text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width
1456
+
1457
+ // replace the nth with two sentinels
1458
+ text = text.replace(/\t/g, '~A~B');
1459
+
1460
+ // use the sentinel to anchor our regex so it doesn't explode
1461
+ text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) {
1462
+ var leadingText = m1,
1463
+ numSpaces = 4 - leadingText.length % 4; // g_tab_width
1464
+
1465
+ // there *must* be a better way to do this:
1466
+ for (var i = 0; i < numSpaces; i++) {
1467
+ leadingText += ' ';
1468
+ }
1469
+
1470
+ return leadingText;
1471
+ });
1472
+
1473
+ // clean up sentinels
1474
+ text = text.replace(/~A/g, ' '); // g_tab_width
1475
+ text = text.replace(/~B/g, '');
1476
+
1477
+ return text;
1478
+
1479
+ });
1480
+
1481
+ /**
1482
+ * Smart processing for ampersands and angle brackets that need to be encoded.
1483
+ */
1484
+ showdown.subParser('encodeAmpsAndAngles', function (text) {
1485
+ 'use strict';
1486
+ // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1487
+ // http://bumppo.net/projects/amputator/
1488
+ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
1489
+
1490
+ // Encode naked <'s
1491
+ text = text.replace(/<(?![a-z\/?\$!])/gi, '&lt;');
1492
+
1493
+ return text;
1494
+ });
1495
+
1496
+ /**
1497
+ * Returns the string, with after processing the following backslash escape sequences.
1498
+ *
1499
+ * attacklab: The polite way to do this is with the new escapeCharacters() function:
1500
+ *
1501
+ * text = escapeCharacters(text,"\\",true);
1502
+ * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1503
+ *
1504
+ * ...but we're sidestepping its use of the (slow) RegExp constructor
1505
+ * as an optimization for Firefox. This function gets called a LOT.
1506
+ */
1507
+ showdown.subParser('encodeBackslashEscapes', function (text) {
1508
+ 'use strict';
1509
+ text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
1510
+ text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback);
1511
+ return text;
1512
+ });
1513
+
1514
+ /**
1515
+ * Encode/escape certain characters inside Markdown code runs.
1516
+ * The point is that in code, these characters are literals,
1517
+ * and lose their special Markdown meanings.
1518
+ */
1519
+ showdown.subParser('encodeCode', function (text) {
1520
+ 'use strict';
1521
+
1522
+ // Encode all ampersands; HTML entities are not
1523
+ // entities within a Markdown code span.
1524
+ text = text.replace(/&/g, '&amp;');
1525
+
1526
+ // Do the angle bracket song and dance:
1527
+ text = text.replace(/</g, '&lt;');
1528
+ text = text.replace(/>/g, '&gt;');
1529
+
1530
+ // Now, escape characters that are magic in Markdown:
1531
+ text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false);
1532
+
1533
+ // jj the line above breaks this:
1534
+ //---
1535
+ //* Item
1536
+ // 1. Subitem
1537
+ // special char: *
1538
+ // ---
1539
+
1540
+ return text;
1541
+ });
1542
+
1543
+ /**
1544
+ * Input: an email address, e.g. "foo@example.com"
1545
+ *
1546
+ * Output: the email address as a mailto link, with each character
1547
+ * of the address encoded as either a decimal or hex entity, in
1548
+ * the hopes of foiling most address harvesting spam bots. E.g.:
1549
+ *
1550
+ * <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1551
+ * x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1552
+ * &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1553
+ *
1554
+ * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1555
+ * mailing list: <http://tinyurl.com/yu7ue>
1556
+ *
1557
+ */
1558
+ showdown.subParser('encodeEmailAddress', function (addr) {
1559
+ 'use strict';
1560
+
1561
+ var encode = [
1562
+ function (ch) {
1563
+ return '&#' + ch.charCodeAt(0) + ';';
1564
+ },
1565
+ function (ch) {
1566
+ return '&#x' + ch.charCodeAt(0).toString(16) + ';';
1567
+ },
1568
+ function (ch) {
1569
+ return ch;
1570
+ }
1571
+ ];
1572
+
1573
+ addr = 'mailto:' + addr;
1574
+
1575
+ addr = addr.replace(/./g, function (ch) {
1576
+ if (ch === '@') {
1577
+ // this *must* be encoded. I insist.
1578
+ ch = encode[Math.floor(Math.random() * 2)](ch);
1579
+ } else if (ch !== ':') {
1580
+ // leave ':' alone (to spot mailto: later)
1581
+ var r = Math.random();
1582
+ // roughly 10% raw, 45% hex, 45% dec
1583
+ ch = (
1584
+ r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
1585
+ );
1586
+ }
1587
+ return ch;
1588
+ });
1589
+
1590
+ addr = '<a href="' + addr + '">' + addr + '</a>';
1591
+ addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part
1592
+
1593
+ return addr;
1594
+ });
1595
+
1596
+ /**
1597
+ * Within tags -- meaning between < and > -- encode [\ ` * _] so they
1598
+ * don't conflict with their use in Markdown for code, italics and strong.
1599
+ */
1600
+ showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) {
1601
+ 'use strict';
1602
+
1603
+ // Build a regex to find HTML tags and comments. See Friedl's
1604
+ // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
1605
+ var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
1606
+
1607
+ text = text.replace(regex, function (wholeMatch) {
1608
+ var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`');
1609
+ tag = showdown.helper.escapeCharacters(tag, '\\`*_', false);
1610
+ return tag;
1611
+ });
1612
+
1613
+ return text;
1614
+ });
1615
+
1616
+ /**
1617
+ * Handle github codeblocks prior to running HashHTML so that
1618
+ * HTML contained within the codeblock gets escaped properly
1619
+ * Example:
1620
+ * ```ruby
1621
+ * def hello_world(x)
1622
+ * puts "Hello, #{x}"
1623
+ * end
1624
+ * ```
1625
+ */
1626
+ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
1627
+ 'use strict';
1628
+
1629
+ // early exit if option is not enabled
1630
+ if (!options.ghCodeBlocks) {
1631
+ return text;
1632
+ }
1633
+
1634
+ text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
1635
+
1636
+ text += '~0';
1637
+
1638
+ text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) {
1639
+ var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
1640
+
1641
+ // First parse the github code block
1642
+ codeblock = showdown.subParser('encodeCode')(codeblock);
1643
+ codeblock = showdown.subParser('detab')(codeblock);
1644
+ codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
1645
+ codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
1646
+
1647
+ codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
1648
+
1649
+ codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
1650
+
1651
+ // Since GHCodeblocks can be false positives, we need to
1652
+ // store the primitive text and the parsed text in a global var,
1653
+ // and then return a token
1654
+ return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
1655
+ });
1656
+
1657
+ // attacklab: strip sentinel
1658
+ text = text.replace(/~0/, '');
1659
+
1660
+ return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
1661
+ });
1662
+
1663
+ showdown.subParser('hashBlock', function (text, options, globals) {
1664
+ 'use strict';
1665
+ text = text.replace(/(^\n+|\n+$)/g, '');
1666
+ return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
1667
+ });
1668
+
1669
+ showdown.subParser('hashElement', function (text, options, globals) {
1670
+ 'use strict';
1671
+
1672
+ return function (wholeMatch, m1) {
1673
+ var blockText = m1;
1674
+
1675
+ // Undo double lines
1676
+ blockText = blockText.replace(/\n\n/g, '\n');
1677
+ blockText = blockText.replace(/^\n/, '');
1678
+
1679
+ // strip trailing blank lines
1680
+ blockText = blockText.replace(/\n+$/g, '');
1681
+
1682
+ // Replace the element text with a marker ("~KxK" where x is its key)
1683
+ blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
1684
+
1685
+ return blockText;
1686
+ };
1687
+ });
1688
+
1689
+ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
1690
+ 'use strict';
1691
+
1692
+ var blockTags = [
1693
+ 'pre',
1694
+ 'div',
1695
+ 'h1',
1696
+ 'h2',
1697
+ 'h3',
1698
+ 'h4',
1699
+ 'h5',
1700
+ 'h6',
1701
+ 'blockquote',
1702
+ 'table',
1703
+ 'dl',
1704
+ 'ol',
1705
+ 'ul',
1706
+ 'script',
1707
+ 'noscript',
1708
+ 'form',
1709
+ 'fieldset',
1710
+ 'iframe',
1711
+ 'math',
1712
+ 'style',
1713
+ 'section',
1714
+ 'header',
1715
+ 'footer',
1716
+ 'nav',
1717
+ 'article',
1718
+ 'aside',
1719
+ 'address',
1720
+ 'audio',
1721
+ 'canvas',
1722
+ 'figure',
1723
+ 'hgroup',
1724
+ 'output',
1725
+ 'video',
1726
+ 'p'
1727
+ ],
1728
+ repFunc = function (wholeMatch, match, left, right) {
1729
+ var txt = wholeMatch;
1730
+ // check if this html element is marked as markdown
1731
+ // if so, it's contents should be parsed as markdown
1732
+ if (left.search(/\bmarkdown\b/) !== -1) {
1733
+ txt = left + globals.converter.makeHtml(match) + right;
1734
+ }
1735
+ return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
1736
+ };
1737
+
1738
+ for (var i = 0; i < blockTags.length; ++i) {
1739
+ text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}<' + blockTags[i] + '\\b[^>]*>', '</' + blockTags[i] + '>', 'gim');
1740
+ }
1741
+
1742
+ // HR SPECIAL CASE
1743
+ text = text.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
1744
+ showdown.subParser('hashElement')(text, options, globals));
1745
+
1746
+ // Special case for standalone HTML comments
1747
+ text = showdown.helper.replaceRecursiveRegExp(text, function (txt) {
1748
+ return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
1749
+ }, '^ {0,3}<!--', '-->', 'gm');
1750
+
1751
+ // PHP and ASP-style processor instructions (<?...?> and <%...%>)
1752
+ text = text.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
1753
+ showdown.subParser('hashElement')(text, options, globals));
1754
+
1755
+ return text;
1756
+ });
1757
+
1758
+ /**
1759
+ * Hash span elements that should not be parsed as markdown
1760
+ */
1761
+ showdown.subParser('hashHTMLSpans', function (text, config, globals) {
1762
+ 'use strict';
1763
+
1764
+ var matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');
1765
+
1766
+ for (var i = 0; i < matches.length; ++i) {
1767
+ text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L');
1768
+ }
1769
+ return text;
1770
+ });
1771
+
1772
+ /**
1773
+ * Unhash HTML spans
1774
+ */
1775
+ showdown.subParser('unhashHTMLSpans', function (text, config, globals) {
1776
+ 'use strict';
1777
+
1778
+ for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
1779
+ text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]);
1780
+ }
1781
+
1782
+ return text;
1783
+ });
1784
+
1785
+ /**
1786
+ * Hash span elements that should not be parsed as markdown
1787
+ */
1788
+ showdown.subParser('hashPreCodeTags', function (text, config, globals) {
1789
+ 'use strict';
1790
+
1791
+ var repFunc = function (wholeMatch, match, left, right) {
1792
+ // encode html entities
1793
+ var codeblock = left + showdown.subParser('encodeCode')(match) + right;
1794
+ return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
1795
+ };
1796
+
1797
+ text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^ {0,3}</code>\\s*</pre>', 'gim');
1798
+ return text;
1799
+ });
1800
+
1801
+ showdown.subParser('headers', function (text, options, globals) {
1802
+ 'use strict';
1803
+
1804
+ text = globals.converter._dispatch('headers.before', text, options, globals);
1805
+
1806
+ var prefixHeader = options.prefixHeaderId,
1807
+ headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
1808
+
1809
+ // Set text-style headers:
1810
+ // Header 1
1811
+ // ========
1812
+ //
1813
+ // Header 2
1814
+ // --------
1815
+ //
1816
+ setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm,
1817
+ setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
1818
+
1819
+ text = text.replace(setextRegexH1, function (wholeMatch, m1) {
1820
+
1821
+ var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
1822
+ hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
1823
+ hLevel = headerLevelStart,
1824
+ hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
1825
+ return showdown.subParser('hashBlock')(hashBlock, options, globals);
1826
+ });
1827
+
1828
+ text = text.replace(setextRegexH2, function (matchFound, m1) {
1829
+ var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
1830
+ hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
1831
+ hLevel = headerLevelStart + 1,
1832
+ hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
1833
+ return showdown.subParser('hashBlock')(hashBlock, options, globals);
1834
+ });
1835
+
1836
+ // atx-style headers:
1837
+ // # Header 1
1838
+ // ## Header 2
1839
+ // ## Header 2 with closing hashes ##
1840
+ // ...
1841
+ // ###### Header 6
1842
+ //
1843
+ text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) {
1844
+ var span = showdown.subParser('spanGamut')(m2, options, globals),
1845
+ hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
1846
+ hLevel = headerLevelStart - 1 + m1.length,
1847
+ header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
1848
+
1849
+ return showdown.subParser('hashBlock')(header, options, globals);
1850
+ });
1851
+
1852
+ function headerId(m) {
1853
+ var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase();
1854
+
1855
+ if (globals.hashLinkCounts[escapedId]) {
1856
+ title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++);
1857
+ } else {
1858
+ title = escapedId;
1859
+ globals.hashLinkCounts[escapedId] = 1;
1860
+ }
1861
+
1862
+ // Prefix id to prevent causing inadvertent pre-existing style matches.
1863
+ if (prefixHeader === true) {
1864
+ prefixHeader = 'section';
1865
+ }
1866
+
1867
+ if (showdown.helper.isString(prefixHeader)) {
1868
+ return prefixHeader + title;
1869
+ }
1870
+ return title;
1871
+ }
1872
+
1873
+ text = globals.converter._dispatch('headers.after', text, options, globals);
1874
+ return text;
1875
+ });
1876
+
1877
+ /**
1878
+ * Turn Markdown image shortcuts into <img> tags.
1879
+ */
1880
+ showdown.subParser('images', function (text, options, globals) {
1881
+ 'use strict';
1882
+
1883
+ text = globals.converter._dispatch('images.before', text, options, globals);
1884
+
1885
+ var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,
1886
+ referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g;
1887
+
1888
+ function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
1889
+
1890
+ var gUrls = globals.gUrls,
1891
+ gTitles = globals.gTitles,
1892
+ gDims = globals.gDimensions;
1893
+
1894
+ linkId = linkId.toLowerCase();
1895
+
1896
+ if (!title) {
1897
+ title = '';
1898
+ }
1899
+
1900
+ if (url === '' || url === null) {
1901
+ if (linkId === '' || linkId === null) {
1902
+ // lower-case and turn embedded newlines into spaces
1903
+ linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
1904
+ }
1905
+ url = '#' + linkId;
1906
+
1907
+ if (!showdown.helper.isUndefined(gUrls[linkId])) {
1908
+ url = gUrls[linkId];
1909
+ if (!showdown.helper.isUndefined(gTitles[linkId])) {
1910
+ title = gTitles[linkId];
1911
+ }
1912
+ if (!showdown.helper.isUndefined(gDims[linkId])) {
1913
+ width = gDims[linkId].width;
1914
+ height = gDims[linkId].height;
1915
+ }
1916
+ } else {
1917
+ return wholeMatch;
1918
+ }
1919
+ }
1920
+
1921
+ altText = altText.replace(/"/g, '&quot;');
1922
+ altText = showdown.helper.escapeCharacters(altText, '*_', false);
1923
+ url = showdown.helper.escapeCharacters(url, '*_', false);
1924
+ var result = '<img src="' + url + '" alt="' + altText + '"';
1925
+
1926
+ if (title) {
1927
+ title = title.replace(/"/g, '&quot;');
1928
+ title = showdown.helper.escapeCharacters(title, '*_', false);
1929
+ result += ' title="' + title + '"';
1930
+ }
1931
+
1932
+ if (width && height) {
1933
+ width = (width === '*') ? 'auto' : width;
1934
+ height = (height === '*') ? 'auto' : height;
1935
+
1936
+ result += ' width="' + width + '"';
1937
+ result += ' height="' + height + '"';
1938
+ }
1939
+
1940
+ result += ' />';
1941
+
1942
+ return result;
1943
+ }
1944
+
1945
+ // First, handle reference-style labeled images: ![alt text][id]
1946
+ text = text.replace(referenceRegExp, writeImageTag);
1947
+
1948
+ // Next, handle inline images: ![alt text](url =<width>x<height> "optional title")
1949
+ text = text.replace(inlineRegExp, writeImageTag);
1950
+
1951
+ text = globals.converter._dispatch('images.after', text, options, globals);
1952
+ return text;
1953
+ });
1954
+
1955
+ showdown.subParser('italicsAndBold', function (text, options, globals) {
1956
+ 'use strict';
1957
+
1958
+ text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
1959
+
1960
+ if (options.literalMidWordUnderscores) {
1961
+ //underscores
1962
+ // Since we are consuming a \s character, we need to add it
1963
+ text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
1964
+ text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
1965
+ //asterisks
1966
+ text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>');
1967
+ text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
1968
+
1969
+ } else {
1970
+ // <strong> must go first:
1971
+ text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>');
1972
+ text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
1973
+ }
1974
+
1975
+ text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
1976
+ return text;
1977
+ });
1978
+
1979
+ /**
1980
+ * Form HTML ordered (numbered) and unordered (bulleted) lists.
1981
+ */
1982
+ showdown.subParser('lists', function (text, options, globals) {
1983
+ 'use strict';
1984
+
1985
+ text = globals.converter._dispatch('lists.before', text, options, globals);
1986
+ /**
1987
+ * Process the contents of a single ordered or unordered list, splitting it
1988
+ * into individual list items.
1989
+ * @param {string} listStr
1990
+ * @param {boolean} trimTrailing
1991
+ * @returns {string}
1992
+ */
1993
+ function processListItems (listStr, trimTrailing) {
1994
+ // The $g_list_level global keeps track of when we're inside a list.
1995
+ // Each time we enter a list, we increment it; when we leave a list,
1996
+ // we decrement. If it's zero, we're not in a list anymore.
1997
+ //
1998
+ // We do this because when we're not inside a list, we want to treat
1999
+ // something like this:
2000
+ //
2001
+ // I recommend upgrading to version
2002
+ // 8. Oops, now this line is treated
2003
+ // as a sub-list.
2004
+ //
2005
+ // As a single paragraph, despite the fact that the second line starts
2006
+ // with a digit-period-space sequence.
2007
+ //
2008
+ // Whereas when we're inside a list (or sub-list), that line will be
2009
+ // treated as the start of a sub-list. What a kludge, huh? This is
2010
+ // an aspect of Markdown's syntax that's hard to parse perfectly
2011
+ // without resorting to mind-reading. Perhaps the solution is to
2012
+ // change the syntax rules such that sub-lists must start with a
2013
+ // starting cardinal number; e.g. "1." or "a.".
2014
+ globals.gListLevel++;
2015
+
2016
+ // trim trailing blank lines:
2017
+ listStr = listStr.replace(/\n{2,}$/, '\n');
2018
+
2019
+ // attacklab: add sentinel to emulate \z
2020
+ listStr += '~0';
2021
+
2022
+ var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
2023
+ isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr));
2024
+
2025
+ // Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
2026
+ // which is a syntax breaking change
2027
+ // activating this option reverts to old behavior
2028
+ if (options.disableForced4SpacesIndentedSublists) {
2029
+ rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm;
2030
+ }
2031
+
2032
+ listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
2033
+ checked = (checked && checked.trim() !== '');
2034
+
2035
+ var item = showdown.subParser('outdent')(m4, options, globals),
2036
+ bulletStyle = '';
2037
+
2038
+ // Support for github tasklists
2039
+ if (taskbtn && options.tasklists) {
2040
+ bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
2041
+ item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () {
2042
+ var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
2043
+ if (checked) {
2044
+ otp += ' checked';
2045
+ }
2046
+ otp += '>';
2047
+ return otp;
2048
+ });
2049
+ }
2050
+
2051
+ // m1 - Leading line or
2052
+ // Has a double return (multi paragraph) or
2053
+ // Has sublist
2054
+ if (m1 || (item.search(/\n{2,}/) > -1)) {
2055
+ item = showdown.subParser('githubCodeBlocks')(item, options, globals);
2056
+ item = showdown.subParser('blockGamut')(item, options, globals);
2057
+ } else {
2058
+ // Recursion for sub-lists:
2059
+ item = showdown.subParser('lists')(item, options, globals);
2060
+ item = item.replace(/\n$/, ''); // chomp(item)
2061
+ if (isParagraphed) {
2062
+ item = showdown.subParser('paragraphs')(item, options, globals);
2063
+ } else {
2064
+ item = showdown.subParser('spanGamut')(item, options, globals);
2065
+ }
2066
+ }
2067
+ item = '<li' + bulletStyle + '>' + item + '</li>\n';
2068
+ return item;
2069
+ });
1435
2070
 
1436
- var escapeCharacters_callback = function(wholeMatch,m1) {
1437
- var charCodeToEscape = m1.charCodeAt(0);
1438
- return "~E"+charCodeToEscape+"E";
2071
+ // attacklab: strip sentinel
2072
+ listStr = listStr.replace(/~0/g, '');
2073
+
2074
+ globals.gListLevel--;
2075
+
2076
+ if (trimTrailing) {
2077
+ listStr = listStr.replace(/\s+$/, '');
2078
+ }
2079
+
2080
+ return listStr;
2081
+ }
2082
+
2083
+ /**
2084
+ * Check and parse consecutive lists (better fix for issue #142)
2085
+ * @param {string} list
2086
+ * @param {string} listType
2087
+ * @param {boolean} trimTrailing
2088
+ * @returns {string}
2089
+ */
2090
+ function parseConsecutiveLists(list, listType, trimTrailing) {
2091
+ // check if we caught 2 or more consecutive lists by mistake
2092
+ // we use the counterRgx, meaning if listType is UL we look for OL and vice versa
2093
+ var olRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?\d+\.[ \t]/gm : /^ {0,3}\d+\.[ \t]/gm,
2094
+ ulRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?[*+-][ \t]/gm : /^ {0,3}[*+-][ \t]/gm,
2095
+ counterRxg = (listType === 'ul') ? olRgx : ulRgx,
2096
+ result = '';
2097
+
2098
+ if (list.search(counterRxg) !== -1) {
2099
+ (function parseCL(txt) {
2100
+ var pos = txt.search(counterRxg);
2101
+ if (pos !== -1) {
2102
+ // slice
2103
+ result += '\n<' + listType + '>\n' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n';
2104
+
2105
+ // invert counterType and listType
2106
+ listType = (listType === 'ul') ? 'ol' : 'ul';
2107
+ counterRxg = (listType === 'ul') ? olRgx : ulRgx;
2108
+
2109
+ //recurse
2110
+ parseCL(txt.slice(pos));
2111
+ } else {
2112
+ result += '\n<' + listType + '>\n' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n';
2113
+ }
2114
+ })(list);
2115
+ } else {
2116
+ result = '\n<' + listType + '>\n' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n';
2117
+ }
2118
+
2119
+ return result;
2120
+ }
2121
+
2122
+ // add sentinel to hack around khtml/safari bug:
2123
+ // http://bugs.webkit.org/show_bug.cgi?id=11231
2124
+ text += '~0';
2125
+
2126
+ if (globals.gListLevel) {
2127
+ text = text.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
2128
+ function (wholeMatch, list, m2) {
2129
+ var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2130
+ return parseConsecutiveLists(list, listType, true);
2131
+ }
2132
+ );
2133
+ } else {
2134
+ text = text.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
2135
+ function (wholeMatch, m1, list, m3) {
2136
+ var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2137
+ return parseConsecutiveLists(list, listType, false);
2138
+ }
2139
+ );
2140
+ }
2141
+
2142
+ // strip sentinel
2143
+ text = text.replace(/~0/, '');
2144
+
2145
+ text = globals.converter._dispatch('lists.after', text, options, globals);
2146
+ return text;
2147
+ });
2148
+
2149
+ /**
2150
+ * Remove one level of line-leading tabs or spaces
2151
+ */
2152
+ showdown.subParser('outdent', function (text) {
2153
+ 'use strict';
2154
+
2155
+ // attacklab: hack around Konqueror 3.5.4 bug:
2156
+ // "----------bug".replace(/^-/g,"") == "bug"
2157
+ text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
2158
+
2159
+ // attacklab: clean up hack
2160
+ text = text.replace(/~0/g, '');
2161
+
2162
+ return text;
2163
+ });
2164
+
2165
+ /**
2166
+ *
2167
+ */
2168
+ showdown.subParser('paragraphs', function (text, options, globals) {
2169
+ 'use strict';
2170
+
2171
+ text = globals.converter._dispatch('paragraphs.before', text, options, globals);
2172
+ // Strip leading and trailing lines:
2173
+ text = text.replace(/^\n+/g, '');
2174
+ text = text.replace(/\n+$/g, '');
2175
+
2176
+ var grafs = text.split(/\n{2,}/g),
2177
+ grafsOut = [],
2178
+ end = grafs.length; // Wrap <p> tags
2179
+
2180
+ for (var i = 0; i < end; i++) {
2181
+ var str = grafs[i];
2182
+ // if this is an HTML marker, copy it
2183
+ if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
2184
+ grafsOut.push(str);
2185
+ } else {
2186
+ str = showdown.subParser('spanGamut')(str, options, globals);
2187
+ str = str.replace(/^([ \t]*)/g, '<p>');
2188
+ str += '</p>';
2189
+ grafsOut.push(str);
2190
+ }
2191
+ }
2192
+
2193
+ /** Unhashify HTML blocks */
2194
+ end = grafsOut.length;
2195
+ for (i = 0; i < end; i++) {
2196
+ var blockText = '',
2197
+ grafsOutIt = grafsOut[i],
2198
+ codeFlag = false;
2199
+ // if this is a marker for an html block...
2200
+ while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
2201
+ var delim = RegExp.$1,
2202
+ num = RegExp.$2;
2203
+
2204
+ if (delim === 'K') {
2205
+ blockText = globals.gHtmlBlocks[num];
2206
+ } else {
2207
+ // we need to check if ghBlock is a false positive
2208
+ if (codeFlag) {
2209
+ // use encoded version of all text
2210
+ blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text);
2211
+ } else {
2212
+ blockText = globals.ghCodeBlocks[num].codeblock;
2213
+ }
2214
+ }
2215
+ blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
2216
+
2217
+ grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
2218
+ // Check if grafsOutIt is a pre->code
2219
+ if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
2220
+ codeFlag = true;
2221
+ }
2222
+ }
2223
+ grafsOut[i] = grafsOutIt;
2224
+ }
2225
+ text = grafsOut.join('\n');
2226
+ // Strip leading and trailing lines:
2227
+ text = text.replace(/^\n+/g, '');
2228
+ text = text.replace(/\n+$/g, '');
2229
+ return globals.converter._dispatch('paragraphs.after', text, options, globals);
2230
+ });
2231
+
2232
+ /**
2233
+ * Run extension
2234
+ */
2235
+ showdown.subParser('runExtension', function (ext, text, options, globals) {
2236
+ 'use strict';
2237
+
2238
+ if (ext.filter) {
2239
+ text = ext.filter(text, globals.converter, options);
2240
+
2241
+ } else if (ext.regex) {
2242
+ // TODO remove this when old extension loading mechanism is deprecated
2243
+ var re = ext.regex;
2244
+ if (!re instanceof RegExp) {
2245
+ re = new RegExp(re, 'g');
2246
+ }
2247
+ text = text.replace(re, ext.replace);
2248
+ }
2249
+
2250
+ return text;
2251
+ });
2252
+
2253
+ /**
2254
+ * These are all the transformations that occur *within* block-level
2255
+ * tags like paragraphs, headers, and list items.
2256
+ */
2257
+ showdown.subParser('spanGamut', function (text, options, globals) {
2258
+ 'use strict';
2259
+
2260
+ text = globals.converter._dispatch('spanGamut.before', text, options, globals);
2261
+ text = showdown.subParser('codeSpans')(text, options, globals);
2262
+ text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
2263
+ text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
2264
+
2265
+ // Process anchor and image tags. Images must come first,
2266
+ // because ![foo][f] looks like an anchor.
2267
+ text = showdown.subParser('images')(text, options, globals);
2268
+ text = showdown.subParser('anchors')(text, options, globals);
2269
+
2270
+ // Make links out of things like `<http://example.com/>`
2271
+ // Must come after _DoAnchors(), because you can use < and >
2272
+ // delimiters in inline links like [this](<url>).
2273
+ text = showdown.subParser('autoLinks')(text, options, globals);
2274
+ text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
2275
+ text = showdown.subParser('italicsAndBold')(text, options, globals);
2276
+ text = showdown.subParser('strikethrough')(text, options, globals);
2277
+
2278
+ // Do hard breaks:
2279
+ text = text.replace(/ +\n/g, ' <br />\n');
2280
+
2281
+ text = globals.converter._dispatch('spanGamut.after', text, options, globals);
2282
+ return text;
2283
+ });
2284
+
2285
+ showdown.subParser('strikethrough', function (text, options, globals) {
2286
+ 'use strict';
2287
+
2288
+ if (options.strikethrough) {
2289
+ text = globals.converter._dispatch('strikethrough.before', text, options, globals);
2290
+ text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '<del>$1</del>');
2291
+ text = globals.converter._dispatch('strikethrough.after', text, options, globals);
2292
+ }
2293
+
2294
+ return text;
2295
+ });
2296
+
2297
+ /**
2298
+ * Strip any lines consisting only of spaces and tabs.
2299
+ * This makes subsequent regexs easier to write, because we can
2300
+ * match consecutive blank lines with /\n+/ instead of something
2301
+ * contorted like /[ \t]*\n+/
2302
+ */
2303
+ showdown.subParser('stripBlankLines', function (text) {
2304
+ 'use strict';
2305
+ return text.replace(/^[ \t]+$/mg, '');
2306
+ });
2307
+
2308
+ /**
2309
+ * Strips link definitions from text, stores the URLs and titles in
2310
+ * hash references.
2311
+ * Link defs are in the form: ^[id]: url "optional title"
2312
+ */
2313
+ showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
2314
+ 'use strict';
2315
+
2316
+ var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;
2317
+
2318
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
2319
+ text += '~0';
2320
+
2321
+ text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
2322
+ linkId = linkId.toLowerCase();
2323
+ globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive
2324
+
2325
+ if (blankLines) {
2326
+ // Oops, found blank lines, so it's not a title.
2327
+ // Put back the parenthetical statement we stole.
2328
+ return blankLines + title;
2329
+
2330
+ } else {
2331
+ if (title) {
2332
+ globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
2333
+ }
2334
+ if (options.parseImgDimensions && width && height) {
2335
+ globals.gDimensions[linkId] = {
2336
+ width: width,
2337
+ height: height
2338
+ };
2339
+ }
2340
+ }
2341
+ // Completely remove the definition from the text
2342
+ return '';
2343
+ });
2344
+
2345
+ // attacklab: strip sentinel
2346
+ text = text.replace(/~0/, '');
2347
+
2348
+ return text;
2349
+ });
2350
+
2351
+ showdown.subParser('tables', function (text, options, globals) {
2352
+ 'use strict';
2353
+
2354
+ if (!options.tables) {
2355
+ return text;
2356
+ }
2357
+
2358
+ var tableRgx = /^ {0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm;
2359
+
2360
+ function parseStyles(sLine) {
2361
+ if (/^:[ \t]*--*$/.test(sLine)) {
2362
+ return ' style="text-align:left;"';
2363
+ } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
2364
+ return ' style="text-align:right;"';
2365
+ } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
2366
+ return ' style="text-align:center;"';
2367
+ } else {
2368
+ return '';
2369
+ }
2370
+ }
2371
+
2372
+ function parseHeaders(header, style) {
2373
+ var id = '';
2374
+ header = header.trim();
2375
+ if (options.tableHeaderId) {
2376
+ id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
2377
+ }
2378
+ header = showdown.subParser('spanGamut')(header, options, globals);
2379
+
2380
+ return '<th' + id + style + '>' + header + '</th>\n';
2381
+ }
2382
+
2383
+ function parseCells(cell, style) {
2384
+ var subText = showdown.subParser('spanGamut')(cell, options, globals);
2385
+ return '<td' + style + '>' + subText + '</td>\n';
2386
+ }
2387
+
2388
+ function buildTable(headers, cells) {
2389
+ var tb = '<table>\n<thead>\n<tr>\n',
2390
+ tblLgn = headers.length;
2391
+
2392
+ for (var i = 0; i < tblLgn; ++i) {
2393
+ tb += headers[i];
2394
+ }
2395
+ tb += '</tr>\n</thead>\n<tbody>\n';
2396
+
2397
+ for (i = 0; i < cells.length; ++i) {
2398
+ tb += '<tr>\n';
2399
+ for (var ii = 0; ii < tblLgn; ++ii) {
2400
+ tb += cells[i][ii];
2401
+ }
2402
+ tb += '</tr>\n';
2403
+ }
2404
+ tb += '</tbody>\n</table>\n';
2405
+ return tb;
2406
+ }
2407
+
2408
+ text = globals.converter._dispatch('tables.before', text, options, globals);
2409
+
2410
+ text = text.replace(tableRgx, function (rawTable) {
2411
+
2412
+ var i, tableLines = rawTable.split('\n');
2413
+
2414
+ // strip wrong first and last column if wrapped tables are used
2415
+ for (i = 0; i < tableLines.length; ++i) {
2416
+ if (/^ {0,3}\|/.test(tableLines[i])) {
2417
+ tableLines[i] = tableLines[i].replace(/^ {0,3}\|/, '');
2418
+ }
2419
+ if (/\|[ \t]*$/.test(tableLines[i])) {
2420
+ tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
2421
+ }
2422
+ }
2423
+
2424
+ var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
2425
+ rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}),
2426
+ rawCells = [],
2427
+ headers = [],
2428
+ styles = [],
2429
+ cells = [];
2430
+
2431
+ tableLines.shift();
2432
+ tableLines.shift();
2433
+
2434
+ for (i = 0; i < tableLines.length; ++i) {
2435
+ if (tableLines[i].trim() === '') {
2436
+ continue;
2437
+ }
2438
+ rawCells.push(
2439
+ tableLines[i]
2440
+ .split('|')
2441
+ .map(function (s) {
2442
+ return s.trim();
2443
+ })
2444
+ );
2445
+ }
2446
+
2447
+ if (rawHeaders.length < rawStyles.length) {
2448
+ return rawTable;
2449
+ }
2450
+
2451
+ for (i = 0; i < rawStyles.length; ++i) {
2452
+ styles.push(parseStyles(rawStyles[i]));
2453
+ }
2454
+
2455
+ for (i = 0; i < rawHeaders.length; ++i) {
2456
+ if (showdown.helper.isUndefined(styles[i])) {
2457
+ styles[i] = '';
2458
+ }
2459
+ headers.push(parseHeaders(rawHeaders[i], styles[i]));
2460
+ }
2461
+
2462
+ for (i = 0; i < rawCells.length; ++i) {
2463
+ var row = [];
2464
+ for (var ii = 0; ii < headers.length; ++ii) {
2465
+ if (showdown.helper.isUndefined(rawCells[i][ii])) {
2466
+
2467
+ }
2468
+ row.push(parseCells(rawCells[i][ii], styles[ii]));
2469
+ }
2470
+ cells.push(row);
2471
+ }
2472
+
2473
+ return buildTable(headers, cells);
2474
+ });
2475
+
2476
+ text = globals.converter._dispatch('tables.after', text, options, globals);
2477
+
2478
+ return text;
2479
+ });
2480
+
2481
+ /**
2482
+ * Swap back in all the special characters we've hidden.
2483
+ */
2484
+ showdown.subParser('unescapeSpecialChars', function (text) {
2485
+ 'use strict';
2486
+
2487
+ text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) {
2488
+ var charCodeToReplace = parseInt(m1);
2489
+ return String.fromCharCode(charCodeToReplace);
2490
+ });
2491
+ return text;
2492
+ });
2493
+
2494
+ var root = this;
2495
+
2496
+ // CommonJS/nodeJS Loader
2497
+ if (typeof module !== 'undefined' && module.exports) {
2498
+ module.exports = showdown;
2499
+
2500
+ // AMD Loader
2501
+ } else if (typeof define === 'function' && define.amd) {
2502
+ define(function () {
2503
+ 'use strict';
2504
+ return showdown;
2505
+ });
2506
+
2507
+ // Regular Browser loader
2508
+ } else {
2509
+ root.showdown = showdown;
1439
2510
  }
2511
+ }).call(this);
1440
2512
 
1441
- } // end of Showdown.converter
1442
-
1443
-
1444
- // export
1445
- if (typeof module !== 'undefined') module.exports = Showdown;
1446
-
1447
- // stolen from AMD branch of underscore
1448
- // AMD define happens at the end for compatibility with AMD loaders
1449
- // that don't enforce next-turn semantics on modules.
1450
- if (typeof define === 'function' && define.amd) {
1451
- define('showdown', function() {
1452
- return Showdown;
1453
- });
1454
- }
2513
+ //# sourceMappingURL=showdown.js.map